Отличается ли структура C, состоящая из всех упакованных полей, кроме первого, от упакованной структуры?

Среди атрибутов общего типа GCC предоставляет packed:

Этот атрибут, прикрепленный к struct[...] определение типа указывает, что каждый из его членов (кроме битовых полей нулевой ширины) размещается для минимизации требуемой памяти. Этоэквивалентно указанию packedатрибут на каждом из членов.

Кроме того, из стандарта C18 (§ 6.7.2.1, 15-17):

Внутри объекта структуры небитовые [...] поля имеют адреса, возрастающие в порядке их объявления. Указатель на объект структуры, преобразованный соответствующим образом, указывает на его начальный член [...], и наоборот. Внутри объекта структуры может быть безымянное заполнение, но не в его начале. [...] В конце структуры может быть безымянный отступ [...].

Итак, учитывая, что в начале структуры нет отступов, используя packedна его первом члене кажется лишним. Взаимно (и в этом моя проблема), используя packedна всех его членах, кроме первого, кажется излишне сложным и эквивалентным использованию packed на самой структуре (мы предполагаем, что все элементы имеют целочисленный тип (ы)).

Однако я несколько раз встречал код, похожий на:

struct S {
    unsigned a;
    unsigned b __attribute__ ((__packed__));
    unsigned c __attribute__ ((__packed__));
    unsigned d __attribute__ ((__packed__));
};

и я не могу понять, почему автор предпочел такую ​​борьбу

struct S {
    unsigned a;
    unsigned b;
    unsigned c;
    unsigned d;
} __attribute__ ((__packed__));

Дело в выравнивании самой конструкции, packedна первый член перешить? Это исходит от чего-то другого, кроме заполнения и выравнивания, о чем я не думал (например, причуда в более старых версиях GCC)? С другой стороны, если они эквивалентны, как я могу быть в этом уверен, учитывая, что документация здесь, ИМХО, мало помогает?

Хотя это не является достоверным доказательством их эквивалентности, я попытался скомпилировать оба structs для разных архитектур (x86, x86_64, Aarch64), и казалось, что они всегда используют одну и ту же схему.

1 ответ

Решение

Если первый член не упакован, он все еще имеет свое первоначальное требование выравнивания, которое может быть больше одного байта, и поэтому структура имеет по крайней мере это требование выравнивания, и это также может потребовать заполнения в конце структуры.

За:

struct S {
    unsigned a;
    unsigned b __attribute__ ((__packed__));
    unsigned c __attribute__ ((__packed__));
    unsigned d __attribute__ ((__packed__));
};

GCC 10.2 для x86-64 говорит _Alignof(struct S) равно 4. В то время как, если a или вся конструкция отмечена __attribute__ ((__packed__));, он говорит, что выравнивание равно 1.

Если мы изменим последний член на char:

struct S {
    unsigned a;
    unsigned b __attribute__ ((__packed__));
    unsigned c __attribute__ ((__packed__));
    char d __attribute__ ((__packed__));
};

тогда GCC сообщает, что его размер составляет 16 байтов, показывая, что он добавил три байта заполнения. Если либо a или вся конструкция отмечена __attribute__ ((__packed__)), размер изменится на 13.

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