простой вопрос … что стандарт говорит о выравнивании элементов структуры?
например с этим:
struct
{
uint8_t a;
uint8_t b;
/* other members */
} test;
Гарантируется, что b находится по смещению 1 от начала структуры?
Спасибо
Стандарт (начиная с C99) ничего не говорит.
Единственные реальные гарантии в том, что (void *)&test == (void *)&a
и что a
находится по более низкому адресу, чем b
, Все остальное зависит от реализации.
C11 6.7.2.1 Структура и объединение спецификаторов р14 говорит
Каждый элемент не битового поля структуры или объекта объединения выравнивается в
определяемый реализацией способ, соответствующий его типу.
Это означает, что вы не можете делать какие-либо переносимые предположения о разнице между адресами a
а также b
,
Должно быть возможно использовать offsetof определить смещение членов.
Для C выравнивание определено реализацией, мы можем видеть, что в проекте стандарта C99 6.7.2.1
Спецификаторы структуры и объединения параграф 12(В С11 это будет параграф 14), в котором говорится:
Каждый элемент не битового поля структуры или объекта объединения выровнен способом, определяемым реализацией, соответствующим его типу.
и параграф 13 говорит:
Внутри объекта структуры, элементы не битовых полей и единицы, в которых битовые поля
проживать имеют адреса, которые увеличиваются в том порядке, в котором они объявлены. Указатель на
Объект структуры, соответствующим образом преобразованный, указывает на его начальный элемент (или, если этот элемент является
битовое поле, затем к единице, в которой он находится), и наоборот. Там может быть безымянный
заполнение внутри объекта структуры, но не в его начале.
и для C ++ у нас есть следующие аналогичные цитаты из раздела проекта стандарта 9.2
Члены класса параграф 13 говорит:
Нестатические члены данных (не объединяющего) класса с одинаковым контролем доступа (раздел 11) распределяются так, чтобы более поздние члены имели более высокие адреса в объекте класса. Порядок распределения нестатических элементов данных с различным контролем доступа не определен (раздел 11). Требования выравнивания реализации могут привести к тому, что два смежных элемента не будут выделяться сразу после друг друга;
и параграф 19 говорит:
Указатель на объект структуры стандартной компоновки, соответствующим образом преобразованный с помощью reinterpret_cast, указывает на его
начальный элемент (или, если этот элемент является битовым полем, то к модулю, в котором он находится), и наоборот. [ Заметка:
Следовательно, в объекте структуры стандартной компоновки может быть безымянный отступ, но не в его начале,
по мере необходимости для достижения соответствующего выравнивания. —Конечная записка]
случай, который вы используете, на самом деле не является краевым, оба uint_8 достаточно малы, чтобы поместиться в одно и то же слово в памяти, и было бы бесполезно помещать каждый uint_8 в uint_16.
Более критический случай будет что-то вроде:
{
uint8_t a;
uint8_t b;
uint_32 c; // where is C, at &a+2 or &a+4 ?
/* other members */
} test;
и в любом случае это всегда будет зависеть от целевой архитектуры и вашего компилятора …
К&Второе издание R (ANSI C) в главе 6.4 (стр. 138) гласит:
Не предполагайте, однако, что размер структуры является суммой размеров ее членов. Из-за требований выравнивания для различных объектов в структуре могут быть безымянные «дыры».
Так что нет, ANSI C не гарантирует, что b
по смещению 1.
Вполне вероятно, что компилятор ставит b
по смещению sizeof(int)
так что он выровнен по размеру машинного слова, с которым легче иметь дело.
Некоторые компиляторы поддерживают вьючные псевдокомментарии так что вы можете заставить, что нет таких «дыр» в struct
, но это не портативно.
То, что гарантировано C-стандартом, уже упоминалось в других ответах.
Тем не менее, чтобы убедиться, b
является по смещению 1
ваш компилятор может предложить варианты «упаковать» структуру, скажет явно добавить нет обивка.
Для GCC это может быть достигнуто #pragma
pack()
.
#pragma pack(1)
struct
{
uint8_t a; /* Guaranteed to be at offset 0. */
uint8_t b; /* Guaranteed to be at offset 1. */
/* other members are guaranteed to start at offset 2. */
} test_packed;
#pragma pack()
struct
{
uint8_t a; /* Guaranteed to by at offset 0. */
uint8_t b; /* NOT guaranteed to be at offset 1. */
/* other members are NOT guaranteed to start at offset 2. */
} test_unpacked;
Переносимым (и сохраняющим) решением было бы просто использовать массив:
struct
{
uint8_t ab[2]; /* ab[0] is guaranteed to be at offset 0. */
/* ab[1] is guaranteed to be at offset 1. */
/* other members are NOT guaranteed to start at offset 2. */
} test_packed;