Рассмотрим 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;
};
В Linux я попытался скомпилировать его и запустить:
$ 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
Что касается А и Б, то все понятно. Но как насчет C? Что добавляет 1 байт к размеру C? Более того, если вы удалите любые отношения наследования, для B или C, C станет размером 4. Используя компиляторы MS (по крайней мере, один, поставляемый с VS 2013), я получил 1, 4 и 4 размера. Любые объяснения и детали по этому поводу приветствуются.
[class.derived] / 7 (стандартный проект)
… Субобъект базового класса может иметь нулевой размер (пункт [класс]); однако два подобъекта, которые имеют один и тот же тип класса и принадлежат к одному и тому же самому производному объекту, не должны размещаться по одному и тому же адресу ([expr.eq]). — конец примечания]
C
имеет подобъекты B::A
(основа участника y
) а также C::A
(прямая база), которые оба типа A
, y
в противном случае может быть по тому же адресу, что и пустая база C
, но так как он также имеет базу того же типа, эта база B
может не иметь один и тот же адрес и поэтому должен быть смещен с помощью отступа. GCC следует этому правилу и применяет только запрос упаковки к заполнению, которое было необходимо для правильного выравнивания.
Других решений пока нет …