Почему sizeof для структуры не равен сумме sizeof её членов?
Почему sizeof для структуры больше суммы размеров полей?
Это не баг, а особенность работы процессора и компилятора, называемая выравниванием памяти (alignment). Компилятор добавляет "пустые" байты (padding) между полями структуры, чтобы ускорить доступ к данным.
Как это работает?
Современные процессоры читают память не побайтово, а блоками по 4 или 8 байт (в зависимости от архитектуры). Если 4-байтовое целое число (int) расположено по адресу, не кратному 4, процессору придётся сделать два обращения к памяти вместо одного. Чтобы этого избежать, компилятор выравнивает каждое поле по его естественной границе:
- char (1 байт) — может быть по любому адресу
- short (2 байта) — по чётному адресу (кратному 2)
- int (4 байта) — по адресу, кратному 4
- double (8 байт) — по адресу, кратному 8
Разберём ваш пример
Ваша структура:
struct MyStruct {
char a; // 1 байт, смещение 0
int b; // 4 байта, нужно смещение, кратное 4
short c; // 2 байта, нужно чётное смещение
};
Компилятор располагает поля так:
- a — байт 0 (смещение 0)
- b — не может начаться с байта 1, т.к. 1 не кратно 4. Компилятор пропускает байты 1, 2, 3 (padding). b занимает байты 4-7
- c — может начаться с байта 8 (чётное). Занимает байты 8-9
Итого: 10 байт данных + 2 байта padding'а = 12 байт? Нет, это не всё! Компилятор также выравнивает размер всей структуры до размера самого большого поля (int — 4 байта). Поэтому добавляет ещё 2 байта в конце (c 10 по 11), чтобы размер стал кратным 4. Итог: 12 байт.
Как изменить порядок полей, чтобы сэкономить память?
Группируйте поля по убыванию размера:
struct OptimizedStruct {
int b; // 4 байта, смещение 0
short c; // 2 байта, смещение 4 (чётное)
char a; // 1 байт, смещение 6
// padding: 1 байт в конце (чтобы размер стал кратным 4)
}; // sizeof = 8 байт
Как узнать выравнивание конкретного типа?
Используйте оператор alignof (C++11):
std::cout << alignof(int) << std::endl; // 4 на x86
Можно ли отключить выравнивание?
Да, с помощью директивы #pragma pack:
#pragma pack(push, 1) // выравнивание по 1 байту
struct PackedStruct {
char a;
int b;
short c;
};
#pragma pack(pop)
// sizeof(PackedStruct) = 7 байт
Но так делать не рекомендуется — доступ к полям будет медленнее, а на некоторых архитектурах (ARM) может вызвать исключение.
Итог
sizeof учитывает padding, который добавляется для производительности. Планируйте порядок полей в структурах, чтобы минимизировать потери памяти, особенно если создаёте много объектов.
Дополнение: как ещё влияет выравнивание?
Первый ответ отлично объяснил основы. Хочу добавить, что выравнивание может зависеть от:
- Архитектуры процессора: на x86-64 int обычно выравнивается по 4 байтам, но компилятор может использовать 8 байт для оптимизации.
- Настроек компилятора: флаги вроде
-malign-doubleв GCC меняют поведение. - Стандарта C++: начиная с C++11, вы можете явно задать выравнивание с помощью
alignas:
struct alignas(16) AlignedStruct {
char a;
int b;
}; // sizeof = 16 байт
Также помните, что наследование и виртуальные функции добавляют свои накладные расходы. Например, класс с виртуальной функцией получает скрытый указатель на vtable (обычно 8 байт на x64), что тоже увеличивает размер.
Совет: если вы работаете с большими массивами структур или передаёте их по сети, используйте #pragma pack(1) или библиотеки сериализации (например, Protocol Buffers). Для обычного кода лучше довериться компилятору — современные процессоры прощают 3-4 байта padding'а ради скорости.
Ответов пока нет.
Войдите чтобы оставить ответ