Расположение памяти структуры в C

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

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

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

Что такое поведение расположения памяти C? Каков должен быть re-ordered/aligned и нет?

76
задан jww 21 September 2018 в 16:57
поделиться

3 ответа

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

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

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

Из C99 §6.7.2.1:

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

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

102
ответ дан 24 November 2019 в 11:08
поделиться

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

  • Члены структуры хранятся в том порядке, в котором они объявлены. (Это требуется стандартом 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;
105
ответ дан 24 November 2019 в 11:08
поделиться

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

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

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

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

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

  1. #pragma pack (n) просто устанавливает новое выравнивание.
  2. #pragma pack () устанавливает выравнивание на то, которое было в эффекте при запуске компиляции (см. также параметр командной строки -fpack-struct [ =] см. Параметры генерации кода).
  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 возвращает макет по умолчанию.
10
ответ дан 24 November 2019 в 11:08
поделиться
Другие вопросы по тегам:

Похожие вопросы: