1

According to this post, the alignment of struct is implementations specific, which implies different compiler will align the members in struct differently, giving different size of the same struct across compilers.

However, in this video, the speaker made it sound like the following structs must have the sizes of 16 and 12 across compilers:

#include <iostream>

struct C {
    uint64_t x;
    uint32_t y;
};

struct D {
    uint32_t x;
    uint32_t y;
    uint32_t z;
};

int main() {
    std::cout << sizeof(C) << std::endl;
    std::cout << sizeof(D) << std::endl;
}

And they are indeed 16 and 12.

Why they have to be 16 and 12? Not 16 and 16?

HCSF
  • 2,387
  • 1
  • 14
  • 40
  • "Why" what? It's not clear what you're asking about. That something *can be* different does not mean that it always *will be* different. – Nicol Bolas Jun 22 '19 at 05:18
  • 1
    Either the speaker is wrong, or you misunderstood him. I think it's very likely that the structs will have the given sizes, but it's absolutely not guaranteed. – john Jun 22 '19 at 05:41
  • Well they will always be at least 16 and 12 on any system that requires alignment for 32 bit and 64 bit integers, that's for sure. Don't know any hardware that doesn't require that myself. – Arne J Jun 22 '19 at 06:24
  • @NicolBolas sorry, didn't ask very clearly. updated. – HCSF Jun 22 '19 at 06:50
  • @ArneJ let's say all system requires 32-bit alignment for `uint32_t` and 64-bit for `uint64_t`. Then, is there a rule defined by C++ standard that what the alignment for each member in an non-packed C struct should be? And padding? As given those 2, exact size can be expected/computed. – HCSF Jun 22 '19 at 06:59
  • The alignment of a member depends only on the type of that member. Also, the size of a type must always be a multiple of its alignment. The alignment of a struct must be a multiple of the biggest member alignment. And every byte that is never conceptually used by the compiler is padding. That should be enough information to calculate the minimum required layout, alignment and size of any struct. And compilers do tend to pick the minimum. – Arne J Jun 22 '19 at 07:09
  • @ArneJ so it sounds like padding and alignment aren't required by the standard but compilers tend to respect alignment and then pad? – HCSF Jun 22 '19 at 07:15
  • One more thing : in C++, any type must at least have size one. And any class with a virtual method keeps at least one pointer to the vtable internally (that is not always clear from the class definition). That has impact on size and alignment. To derive from a class usually means the base class and the members in the derived class are combined in a new C structure, which also has an impact. Last but not least, read some documentation on "empty base class optimization". – Arne J Jun 22 '19 at 07:19
  • You're wrong, alignment and padding is required by the standard. C was designed to execute instructions rapidly. With a packed structure reading a 32 bit integer may require more than a single 'read' instruction from the cpu : there may be 2 reads, then some bitshifting and bitwise or operations. Not efficient at all. – Arne J Jun 22 '19 at 07:25
  • @ArneJ so the rule for alignment and padding defined by standard is what you said, "The alignment of a member depends only on the type of that member. Also, the size of a type must always be a multiple of its alignment. The alignment of a struct must be a multiple of the biggest member alignment."? – HCSF Jun 22 '19 at 07:35
  • 1
    http://www.catb.org/esr/structure-packing/ – Arne J Jun 22 '19 at 07:42

1 Answers1

1

And they are indeed 16 and 12.

They are of this size, given:

  • the compiler (& options) you used
  • your target platform
  • the absence of directives related to aligment/packing in the code

For your video, I guess the speaker just took a given platform/toolchain to develop his example. However in general, since sizeof(T) is compiler/platform dependent, std::atomic<T>::is_lock_free() is also compiler/platform dependent.

Examples

Using the following struct:

struct C {
    uint64_t x;
    uint32_t y;
};

Different compilers & options

Target platform

Alignment/packing directives

Why these differences ?

Compilers are free to add unused bits/bytes after any field of a structure/class. They do so for performance reasons: on some platforms, it is faster to read/write a multi-byte int that verify certain alignment properties (usually, the address of a N-bytes int must be divisible by N).

Usually, it's convenient that your C++ compiler performs low level optimizations behind your back. Sometimes you want more control on this feature (non-exhaustive list of reasons):

  • when you serialize data which will be read by a different program (save to file, send to the network).
  • when memory usage is more important than execution speed.
  • in multicore & multithreaded program, controlling how many struct there are in a CPU cache line can limit cache invalidations between cores, which improves execution speed.

Which is why compilers usually provides utilities to control it.

TL;DR

The sizeof(T) doesn't "have" to be anything for a given T. It is compiler/platform dependent, and you can often override it with specific compiler directives.