Packed struct size is 40 even though member sizes add to 36

102 Views Asked by At

struct K is packed and the sum of it's class member sizes is 36. Each member has a static_assert checking this.

However, the size of K itself is 40, not 36. K isn't polymorphic etc.

Why is K not size 36?

Using Clang 16. I'm not aware of an easy way to visualize the struct, or I would have. If anyone can recommend a way that would be great.

template<class S>
struct P
{};

struct D : public P<D>
{
    uint64_t x_;
};

enum class E : uint8_t
{};

struct K
{
    D a_;          
    D b_;          
    uint64_t c_;   
    uint64_t d_;   
    uint16_t e_;   
    E f_;          
    E g_;          

    static_assert(sizeof(a_) == 8); // Passes
    static_assert(sizeof(b_) == 8); // Passes
    static_assert(sizeof(c_) == 8); // Passes
    static_assert(sizeof(d_) == 8); // Passes
    static_assert(sizeof(e_) == 2); // Passes
    static_assert(sizeof(f_) == 1); // Passes
    static_assert(sizeof(g_) == 1); // Passes

}__attribute__((packed));

static_assert(sizeof(K) == 36); // Fails, is 40 instead of 36

int main()
{
    K f;
}
1

There are 1 best solutions below

0
Jan Schultke On

With warnings enabled for clang, the issue becomes obvious:

<source>:17:7: warning: not packing field 'a_' as it is non-POD
               for the purposes of layout [-Wpacked-non-pod]
   17 |     D a_;          
      |       ^
<source>:18:7: warning: not packing field 'b_' as it is non-POD
               for the purposes of layout [-Wpacked-non-pod]
   18 |     D b_;          
      |       ^

Neither GCC nor clang seem to document this behavior (see GCC packed attribute, clang packed attribute), but it looks like using inheritance (from P<D>) disables the effect of packed for a member. There is no inherent reason why D couldn't be packed in this case, it simply appears to be a general ban.

If any member isn't packed, to ensure proper alignment of all members, the struct requires additional padding. The alignment of K is 8, as evidenced by:

static_assert(alignof(K) == 8); // passes; alignment is normally 1 for packed types

Since K is aligned to 8 bytes, and the combined size of its members is 36, 4 padding bytes have to be added at the end to correctly align K to 8 bytes (by increasing its size to 40).


See Also