Структура памяти макета в C
У меня есть фон C#. Я очень новичок в языке низкого уровня, таком как C.
В C# struct
Память по умолчанию компилятором. Компилятор может неупорядоченно переупорядочивать поля данных или добавлять дополнительные биты между полями. Итак, мне пришлось указать какой-то специальный атрибут, чтобы переопределить это поведение для точного макета.
AFAIK, C не переупорядочивает и не выравнивает макет памяти struct
по умолчанию. Однако я слышал, что есть небольшое исключение, которое очень трудно найти.
Каково поведение макета памяти C? Что нужно переупорядочить / выровнять, а что нет?
3 ответа
In C, the compiler is allowed to dictate some alignment for every primitive type. Typically the alignment is the size of the type. But it's entirely implementation-specific.
Padding bytes are introduced so every object is properly aligned. Reordering is not allowed.
Possibly every remotely modern compiler implements #pragma pack
which allows control over padding and leaves it to the programmer to comply with the ABI. (It is strictly nonstandard, though.)
From C99 §6.7.2.1:
12 Each non-bit-field member of a structure or union object is aligned in an implementation- defined manner appropriate to its type.
13 В объекте структуры члены, не являющиеся битовыми полями, и блоки, в которых находятся битовые поля, имеют адреса, которые увеличиваются в порядке их объявления. Указатель на объект структуры, соответствующим образом преобразованный, указывает на его начальный элемент (или, если этот элемент является битовым полем, то на модуль, в котором он находится), и наоборот. Внутри объекта структуры может быть безымянный отступ, но не в его начале.
Это зависит от реализации, но на практике это правило (при отсутствии #pragma pack
или тому подобное)
- Члены структуры хранятся в порядке их объявления. (Это требуется стандартом 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, сразу после- В конце добавляются 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[=], см. Опции Code Gen).пакет pragma (push[,n]) помещает текущие настройки выравнивания на
внутренний стек, а затем опционально устанавливает новое выравнивание.пакет pragma (pop) восстанавливает настройку выравнивания, сохраненную в
вершина внутреннего стека (и удаляет эту запись стека). Обратите внимание, чтоenter code here
#pragma pack([n]) не влияет на этот внутренний стек; таким образом, можно получить пакет #pragma (push), за которым следуют несколько экземпляров #pragma pack(n) и завершить их одним пакетом #pragma (pop).Некоторые цели, например i386 и powerpc, поддерживают ms_struct #pragma, которая представляет структуру в виде документированного __attribute__ ((ms_struct)).
Прагма ms_struct включает очереди для объявленных структур.
Прагма ms_struct off отключает макет для объявленных структур.
Сброс прагмы ms_struct восходит к макету по умолчанию.