Структура памяти макета в C - PullRequest
73 голосов
/ 01 мая 2010

У меня есть фон C #. Я очень новичок в низкоуровневом языке, таком как C.

В C # память struct распределяется компилятором по умолчанию. Компилятор может неупорядоченно изменять порядок полей данных или добавлять дополнительные биты между полями. Поэтому мне пришлось указать какой-то специальный атрибут, чтобы переопределить это поведение для точного макета.

AFAIK, C не переупорядочивает и не выравнивает структуру памяти struct по умолчанию. Однако я слышал, что есть небольшое исключение, которое очень трудно найти.

Каково поведение C в плане памяти? Что следует переупорядочить / выровнять, а не?

Ответы [ 3 ]

102 голосов
/ 01 мая 2010

Это зависит от реализации, но на практике правило (при отсутствии #pragma pack или подобного):

  • Элементы Struct хранятся в том порядке, в котором они были объявлены. (Это требуется стандартом C99, как упоминалось здесь ранее.)
  • При необходимости перед каждым элементом структуры добавляется отступ, чтобы обеспечить правильное выравнивание.
  • Каждый тип примитива T требует выравнивания sizeof(T) байтов.

Итак, учитывая следующую структуру:

struct ST
{
   char ch1;
   short s;
   char ch2;
   long long ll;
   int i;
};
  • ch1 по смещению 0
  • вставляется дополнительный байт для выравнивания ...
  • s со смещением 2
  • ch2 по смещению 4, сразу после s
  • 3 байта заполнения вставлены для выравнивания ...
  • ll по смещению 8
  • i по смещению 16, сразу после ll
  • В конце добавляются 4 байта заполнения, так что общая структура кратна 8 байтам. Я проверил это в 64-битной системе: 32-битные системы могут позволять структурам иметь 4-байтовое выравнивание.

Так sizeof(ST) равно 24.

Его можно уменьшить до 16 байтов, переставив элементы, чтобы избежать заполнения:

struct ST
{
   long long ll; // @ 0
   int i;        // @ 8
   short s;      // @ 12
   char ch1;     // @ 14
   char ch2;     // @ 15
} ST;
99 голосов
/ 01 мая 2010

В C компилятору разрешено определять выравнивание для каждого типа примитива. Обычно выравнивание - это размер шрифта. Но это полностью зависит от реализации.

Добавлены байты заполнения, чтобы каждый объект был правильно выровнен. Изменение порядка не допускается.

Возможно, каждый удаленно современный компилятор реализует #pragma pack, который позволяет контролировать заполнение и оставляет программисту возможность соблюдать ABI. (Хотя это строго нестандартно.)

Из C99 §6.7.2.1:

12 Каждый член не битового поля структура или объединение объекта выровнены в соответствии с реализацией соответствует его типу.

13 в пределах структура объекта, не битовое поле члены и единицы, в которых битовые поля имеют адреса, которые увеличение в порядке, в котором они объявлены. Указатель на структуру объект, соответствующим образом преобразованный, указывает на его начальный член (или если этот член битовое поле, то к блоку в который он проживает), и наоборот. Там может быть безымянный отступ в пределах структура объекта, но не на его начало.

9 голосов
/ 01 мая 2010

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

Из статьи Википедии :

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

С 6.54.8 Прагмы структурной упаковки документации GCC:

Для совместимости с Microsoft Компиляторы Windows, GCC поддерживает множество директив #pragma, которые изменяют максимальное выравнивание членов структуры (кроме нулевой ширины битовые поля), союзы и классы впоследствии определено. Значение n ниже всегда требуется быть маленьким Степень двойки и определяет новый выравнивание в байтах.

  1. #pragma pack(n) просто устанавливает новое выравнивание.
  2. #pragma pack() устанавливает выравнивание на тот, который был в эффект при запуске компиляции (см. также опция командной строки -fpack-struct [=] см. Опции кода Gen).
  3. #pragma pack(push[,n]) выдвигает текущую настройку выравнивания на внутренний стек, а затем по желанию устанавливает новое выравнивание.
  4. #pragma pack(pop) восстанавливает настройку выравнивания, сохраненную в вершина внутреннего стека (и удаляет эту запись стека). Обратите внимание, что #pragma pack([n]) не влияет на этот внутренний стек; таким образом это можно иметь #pragma pack(push) с последующим кратным #pragma pack(n) экземпляры и завершены одним #pragma pack(pop).

Некоторые цели, например i386 и powerpc, поддержка ms_struct #pragma, которая излагает структуру как документально __attribute__ ((ms_struct)).

  1. #pragma ms_struct on включает макет для объявленных структур.
  2. #pragma ms_struct off отключает макет для объявленных структур.
  3. #pragma ms_struct reset возвращается к макету по умолчанию.
...