Поиск…


Вступление

По умолчанию компиляторы C выстраивают структуры так, чтобы к каждому участнику можно было получить быстрый доступ, без каких-либо штрафов за «неудовлетворенный доступ», проблему с RISC-машинами, такими как DEC Alpha и некоторые ARM-процессоры.

В зависимости от архитектуры процессора и компилятора структура может занимать больше места в памяти, чем сумма размеров ее компонентов. Компилятор может добавлять дополнения между членами или в конце структуры, но не в начале.

Упаковка отменяет заполнение по умолчанию.

замечания

У Эрика Раймонда есть статья о The Lost Art of C Structure Packing, которая является полезным чтением.

Упаковочные конструкции

По умолчанию структуры заполняются на C. Если вы хотите избежать такого поведения, вы должны явно запросить его. В GCC это __attribute__((__packed__)) . Рассмотрим этот пример на 64-битной машине:

struct foo {
    char *p;  /* 8 bytes */
    char c;   /* 1 byte  */
    long x;   /* 8 bytes */
};

Структура будет автоматически дополнена, чтобы иметь 8-byte выравнивание и будет выглядеть так:

struct foo {
    char *p;     /* 8 bytes */
    char c;      /* 1 byte  */

    char pad[7]; /* 7 bytes added by compiler */

    long x;      /* 8 bytes */
};

Поэтому sizeof(struct foo) даст нам 24 вместо 17 . Это произошло из-за 64-битного компилятора чтения / записи из / в память в 8 байтах слова на каждом шаге и очевидного при попытке написать char c; один байт в памяти получает 8 байтов (т.е. слово) и потребляет только первый байт, а семь его байтов остаются пустыми и недоступны для любой операции чтения и записи для заполнения структуры.

Структурная упаковка

Но если вы добавите атрибут packed , компилятор не добавит дополнения:

struct __attribute__((__packed__)) foo {
    char *p;  /* 8 bytes */
    char c;   /* 1 byte  */
    long x;   /* 8 bytes */
};

Теперь sizeof(struct foo) вернет 17 .

Используются обычно упакованные структуры:

  • Чтобы сэкономить место.
  • Форматировать структуру данных для передачи по сети без зависимости от каждого выравнивания архитектуры каждого узла сети.

Следует иметь в виду, что некоторые процессоры, такие как ARM Cortex-M0, не позволяют использовать неравномерный доступ к памяти; в таких случаях структура упаковки может привести к неопределенному поведению и может привести к сбою процессора.

Наложение структуры

Предположим, что эта struct определена и скомпилирована с 32-битным компилятором:

struct test_32 {
    int a;      // 4 byte
    short b;    // 2 byte
    int c;      // 4 byte    
} str_32;

Мы могли бы ожидать, что эта struct займет всего 10 байт памяти, но, печатая sizeof(str_32) мы видим, что она использует 12 байтов.

Это произошло потому, что компилятор выравнивает переменные для быстрого доступа. Общий шаблон состоит в том, что когда базовый тип занимает N байтов (где N - это мощность 2, такая как 1, 2, 4, 8, 16 - и редко больше), переменная должна быть выровнена на N-байтовой границе ( кратное N байтам).

Для структуры, показанной с sizeof(int) == 4 и sizeof(short) == 2 , общий макет:

  • int a; хранится при смещении 0; размер 4.
  • short b; хранится со смещением 4; размер 2.
  • неназванное заполнение со смещением 6; размер 2.
  • int c; хранится со смещением 8; размер 4.

Таким образом, struct test_32 занимает 12 байт памяти. В этом примере нет отступающих отступов.

Компилятор обеспечит сохранение любых переменных struct test_32 , начиная с 4-байтной границы, так что члены внутри структуры будут правильно выровнены для быстрого доступа. Функции распределения памяти, такие как malloc() , calloc() и realloc() , необходимы для обеспечения того, чтобы возвращаемый указатель был достаточно хорошо выровнен для использования с любым типом данных, поэтому динамически распределенные структуры также будут правильно выровнены.

Вы можете столкнуться с нечетными ситуациями, например, на 64-разрядном процессоре Intel x86_64 (например, Intel Core i7 - Mac, работающем под управлением MacOS Sierra или Mac OS X), где при компиляции в 32-разрядном режиме компиляторы размещают double выравнивание на 4-байтовая граница; но, на том же аппаратном обеспечении, при компиляции в 64-битном режиме компиляторы располагают double выравниванием по 8-байтовой границе.



Modified text is an extract of the original Stack Overflow Documentation
Лицензировано согласно CC BY-SA 3.0
Не связан с Stack Overflow