Почему #pragma pack также влияет на собственное выравнивание структур? - PullRequest
1 голос
/ 24 февраля 2020

Я заметил, что когда #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, что означает, что прагма также имела эффект выравнивания (1) на структуру.

Почему это так? Это определенное поведение?

Ответы [ 2 ]

4 голосов
/ 24 февраля 2020

На выравнивание структуры влияют требования выравнивания ее элементов. Структура в целом обычно выравнивается по выравниванию ее наибольшего члена. Поскольку ваша структура содержала uint32, она была бы выровнена по четырем байтам, если бы вы ранее не вызывали #pragma.

Однако, с #pragma pack(1) вы заставляете выравнивание, требуемое для всех его членов: 1 байт (или без выравнивания) и, следовательно, структура теперь может начинаться с любого адреса в памяти, необязательно с кратностью четырех байтов.

2 голосов
/ 24 февраля 2020

Прежде всего, обратите внимание, что переменная n не требуется выделять просто потому, что она volatile. Поскольку ваша программа не ссылается на эту переменную, компилятор не может сделать что-либо значимое с ней, и она не выделяется. Удаление n из вашей программы приводит к тому же выводу, так что это не объясняет «выключено на 1».

Как упоминалось в комментариях, упаковка 1 не имеет никакого смысла для структуры с один uint32_t член. Эта прагма, однако, меняет требование выравнивания для структуры с 4 на 1. Вы можете проверить это с помощью C11 _Alignof(TEST).

Это, в свою очередь, означает, что компилятор может свободно размещать структуру по любому адресу, который ему нравится. Очевидно, есть что-то еще с размером 1 байт, выделенным в том же сегменте памяти, что и ваша переменная в данной системе, и поэтому вашей структуре просто передали следующий доступный адрес. «CRT» (стартовый код), а также стандартные функции lib могут нуждаться в размещении переменных сверх тех, которые явно объявлены программистом.

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

...