Вопрос

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

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

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

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

Это было полезно?

Решение

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

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

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

Из C99 §6.7.2.1:

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

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

Другие советы

Это специфично для реализации, но на практике правило (в отсутствие #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;

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

От Википедия статья:

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

От 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 Возвращается к макету по умолчанию.
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top