Consider a MWE:
#include <iostream>
struct A {} __attribute__((packed)) ;
struct B : A { int x; } __attribute__((packed)) ;
struct C : A { B y; } __attribute__((packed)) ;
int main () {
std::cout << "Size of A: " << sizeof(A) << std::endl;
std::cout << "Size of B: " << sizeof(B) << std::endl;
std::cout << "Size of C: " << sizeof(C) << std::endl;
};
On Linux I tried to compile it and run:
$ g++ --version
g++ (Ubuntu 5.4.0-6ubuntu1~16.04.4) 5.4.0 20160609
$ g++ ./test.cpp && ./a.out
Size of A: 1
Size of B: 4
Size of C: 5
As for A and B everything is clear. But what about C? What adds 1 byte to the C's size? Moreover, if you'll remove any inheritance relationships, either for B or C, C become of size 4. Using MS compilers (at least one that is shipped with VS 2013), I got 1, 4 and 4 sizes. Any explanation and details on that are appreciated.
[class.derived] / 7 (standard draft)
... A base class subobject may be of zero size (Clause [class]); however, two subobjects that have the same class type and that belong to the same most derived object must not be allocated at the same address ([expr.eq]). — end note ]
C
has subobjects B::A
(base of member y
) and C::A
(direct base) which are both of type A
. y
could otherwise be at the same address as the the empty base of C
, but since it also has a base of same type, that base of B
may not have the same address and therefore must be offset with padding. GCC follows this rule and only applies packing request to padding that was needed for correct alignment.