Выравнивание и размер структуры

Есть у нас, допустим структура.

struct my_struct{
  int a;
  char b;
  int c;
};

Но если на разных платформах ( например avr и arm ) выполнить следующий код

printf("размер структуры %u\r\n", sizeof( struct my_struct ) );

то мы получим разные результаты. Для arm размер структуры равен 12, а для avr равен 5. Почему?

На первый взгляд, потому, что переменные типа int на различных платформах имеют разную разрядность. Для arm это 4 байта, для avr 2 байта.

Перепишем нашу структуру.

struct my_struct{
  short a;
  char b;
  short c;
};

Тип short имеет размер 2 байта на всех платформах.

Но мы опять получаем разный размер структуры, для arm 6 байт, для avr 5 байт.

Дело в том что компилятор gcc (да и большинство других) по умолчанию выравнивает поля структуры (дополняет неиспользуемыми байтами) в соответствии с следующим правилом: каждое поле выравнивается по адресу, кратному размеру данного поля. Поле типа int на 32-битной системе будет выровнено по границе 4 байт, short по границе 2 байта. Поля типа char не выравниваются. Размер структуры выравнивается до размера, кратному размеру его максимального элемента.

Но как нам быть, если мы хотим передать структуру с одной платформы на другую.

Устранить поведение по умолчанию и убрать выравнивание можно если указать
атрибут packed при описании структуры, директива компилятора #pragma pack(1) также может использоваться для этой цели

struct my_struct{
  short a;
  char b;
  short c;
}__attribute__((__packed__ ));

В этом случае «пустоты», связанные с выравниванием, исчезают и размер структуры на разных платформах становиться одинаковым.

Если у нас много экземпляров структуры, то отключив выравнивание можно сэкономить память. Отрицательной стороной отключения выравнивания является то, что при этом увеличивается программный код и время необходимые для обработки структур.

Запись опубликована в рубрике Великий и могучий Си с метками , . Добавьте в закладки постоянную ссылку.

4 комментария: Выравнивание и размер структуры

  1. Санек говорит:

    Спасибо,классная статья!

  2. Артём Двинин говорит:

    Пожалуйста! ;)

  3. Дмитрий говорит:

    Особенно это актуально когда делаешь обмен по UARTу или чему то подобному между разными платформами (и описываешь команды структурами), иначе могут возникнуть чудеса.

    Для блоков можно делать так
    #pragma push() // запоминаем текущее выравнивание
    #pragma pack(1)
    ..
    .. тут всякие структуры
    ..
    #pragma pop() // восстанавливаем выравнивание

  4. Игорь говорит:

    Это разве статья, так заметка. А всё таки char будет выравниваться или нет?

Добавить комментарий

Ваш e-mail не будет опубликован.

Можно использовать следующие HTML-теги и атрибуты: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>