Почему #pragma pack также влияет на собственное выравнивание структур?
Я заметил, что когда вокруг структуры используется #pragma pack, это влияет не только на выравнивание внутри нее, но и на выравнивание самой структуры. Учтите следующее:
#include <stdio.h>
#include <stdint.h>
#pragma pack(1)
typedef struct _TEST
{
uint32_t a;
} TEST;
#pragma pack()
volatile uint8_t n;
TEST b;
int main()
{
printf("Address %lX rem %lu\n", (long unsigned int)&b, (long unsigned int)(&b)%(sizeof(int)));
return 0;
}
Вы можете попробовать этот код здесь: https://onlinegdb.com/BkebdxZEU
Программа вернулась Address 601041 rem 1
, что означает, что прагма также оказала влияние на структуру struct.
Это почему? Это определенное поведение?
2 ответа
На выравнивание конструкции влияют требования к выравниванию ее элементов. Конструкция в целом обычно выравнивается по выравниванию ее самого большого элемента. Поскольку ваша структура содержалаuint32
, он был бы выровнен по четырем байтам, если бы вы не вызвали #pragma раньше.
Однако с #pragma pack(1)
, вы принудительно устанавливаете выравнивание, необходимое для всех его членов, до 1 байта (или без выравнивания), и, следовательно, структура теперь может начинаться с любого адреса в памяти, не обязательно кратного четырем байтам.
Прежде всего, обратите внимание, что переменная n
не нужно выделять просто потому, что это volatile
. Поскольку ваша программа не ссылается на эту переменную, компилятор не может сделать с ней что-либо значимое, и она не выделяется. Удалениеn
из вашей программы дает тот же результат, поэтому это не объясняет "выключение на 1".
Как упоминалось в комментариях, упаковка 1 не имеет смысла для структуры с одним uint32_t
член. Однако эта прагма изменяет требование выравнивания для структуры с 4 на 1. Вы можете проверить это с помощью C11._Alignof(TEST)
.
Это, в свою очередь, означает, что компилятор может разместить структуру по любому адресу, который ему нравится. По-видимому, есть что-то еще с размером 1 байт, выделенное в том же сегменте памяти, что и ваша переменная в данной системе, и поэтому вашей структуре просто был передан следующий доступный адрес. "CRT" (код запуска), а также стандартные функции библиотеки могут нуждаться в размещении переменных помимо тех, которые явно объявлены программистом.
Примечательно, что неправильный доступ замедляет выполнение кода на многих системах и может вызвать сбой программы на других.