Размер структуры больше, чем сумма ее частей из-за того, что называется упаковкой. Определенный процессор имеет предпочтительный размер данных, с которым он работает. Предпочтительный размер большинства современных процессоров - 32 бита (4 байта). Доступ к памяти, когда данные находятся на границе такого типа, более эффективен, чем доступ к границам такого размера.
Например. Рассмотрим простую структуру:
struct myStruct
{
int a;
char b;
int c;
} data;
Если машина представляет собой 32-разрядную машину и данные выровнены по 32-разрядной границе, мы видим непосредственную проблему (при условии отсутствия выравнивания структуры). В этом примере предположим, что данные структуры начинаются с адреса 1024 (0x400 - обратите внимание, что 2 младших бита равны нулю, поэтому данные выровнены по 32-битной границе). Доступ к data.a будет работать нормально, потому что он начинается на границе - 0x400. Доступ к data.b также будет работать нормально, поскольку он находится по адресу 0x404 - еще одна 32-разрядная граница. Но не выровненная структура поместит data.c по адресу 0x405. 4 байта data.c находятся в 0x405, 0x406, 0x407, 0x408. На 32-разрядной машине система считывает data.c в течение одного цикла памяти, но получает только 3 из 4 байтов (4-й байт находится на следующей границе). Таким образом, системе потребуется второй доступ к памяти, чтобы получить 4-й байт,
Теперь, если вместо того, чтобы поместить data.c по адресу 0x405, компилятор дополнил структуру на 3 байта и поместил data.c по адресу 0x408, тогда системе потребовался бы только 1 цикл для чтения данных, сокращая время доступа этот элемент данных на 50%. Заполнение заменяет эффективность памяти на эффективность обработки. Учитывая, что у компьютеров может быть огромное количество памяти (много гигабайт), компиляторы считают, что обмен (скорость по размеру) является разумным.
К сожалению, эта проблема становится опасной, когда вы пытаетесь отправить структуры по сети или даже записать двоичные данные в двоичный файл. Заполнение, вставленное между элементами структуры или класса, может нарушить данные, отправляемые в файл или сеть. Для того чтобы написать переносимый код (который будет использоваться несколькими различными компиляторами), вам, вероятно, придется обращаться к каждому элементу структуры отдельно, чтобы обеспечить надлежащую «упаковку».
С другой стороны, разные компиляторы имеют разные возможности для управления упаковкой структуры данных. Например, в Visual C / C ++ компилятор поддерживает команду #pragma pack. Это позволит вам настроить упаковку и выравнивание данных.
Например:
#pragma pack 1
struct MyStruct
{
int a;
char b;
int c;
short d;
} myData;
I = sizeof(myData);
Теперь у меня должна быть длина 11. Без прагмы я мог бы быть любым от 11 до 14 (а для некоторых систем - до 32), в зависимости от упаковки компилятора по умолчанию.