Почему #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" (код запуска), а также стандартные функции библиотеки могут нуждаться в размещении переменных помимо тех, которые явно объявлены программистом.

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

Другие вопросы по тегам