Структура памяти в C
-
02-10-2019 - |
Вопрос
У меня есть фон 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
в смещении 2ch2
находится в смещении 4, сразу после s- 3 байта заполнения вставлены, чтобы выровнять ...
ll
в смещении 8i
находится в смещении 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 ниже всегда должно быть небольшой мощностью двух и указывает новое выравнивание в байтах.
#pragma pack(n)
Просто устанавливает новое выравнивание.#pragma pack()
Устанавливает выравнивание на тот, который действовал, когда началась компиляция (см. Также опцию командной строки -fpack -struct [=] см. Параметры кода Gen).#pragma pack(push[,n])
Толкает текущую настройку выравнивания на внутреннем стеке, а затем, опционально устанавливает новое выравнивание.#pragma pack(pop)
Восстанавливает настройку выравнивания до того, что сохраняется в верхней части внутреннего стека (и удаляет эту запись стека). Обратите внимание, что#pragma pack([n])
не влияет на этот внутренний стек; Таким образом, это возможно#pragma pack(push)
с последующим несколькими#pragma pack(n)
экземпляры и завершен одним#pragma pack(pop)
.Некоторые цели, например, I386 и PowerPC, поддерживайте MS_STRUCT
#pragma
который излагает структуру как задокументированную__attribute__ ((ms_struct))
.
#pragma ms_struct on
включает макет для объявленных структур.#pragma ms_struct off
Выключает макет для объявленных структур.#pragma ms_struct reset
Возвращается к макету по умолчанию.