Является ли размер структуры требуемым, чтобы быть точным кратным выравниванию этой структуры?

Еще раз, я допрашиваю давнюю веру.

До сегодняшнего дня я считал, что выравнивание следующей структуры обычно будет 4, а размер обычно равен 5 …

struct example { int m_Assume_32_Bits; char m_Assume_8_Bit_Bytes; }; 

Из-за этого предположения у меня есть код структуры данных, который использует offsetof для определения расстояния в байтах между двумя соседними элементами в массиве. Сегодня я заметил какой-то старый код, который использовал sizeof, где он не должен был, не мог понять, почему у меня не было ошибок, закодирован единичный тест – и тест удивил меня, пройдя.

Немного исследования показали, что размер типа I, который использовался для теста (аналогичный структуре выше), был точным кратным выравниванию, т. Е. 8 байтов. После последнего участника у него было дополнение. Вот пример того, почему я этого не ожидал …

 struct example2 { example m_Example; char m_Why_Cant_This_Be_At_Offset_6_Bytes; }; 

Немного о Googling показал примеры, которые дают понять, что это дополнение после окончательного участника разрешено – например, http://en.wikipedia.org/wiki/Data_structure_alignment#Data_structure_padding (бит «или в конце структуры») ,

Это немного неловко, поскольку я недавно опубликовал этот комментарий. Использование прокладки структуры (мой первый комментарий к этому ответу).

То, что я не могу определить, заключается в том, гарантируется ли это дополнение к точному краю выравнивания стандартом C ++, или это то, что разрешено и что некоторые (но, возможно, не все) компиляторы делают.

Итак – размер структуры, который должен быть точным кратным выравниванию этой структуры в соответствии со стандартом C ++?

Если стандарт C дает разные гарантии, меня это тоже интересует, но основное внимание уделяется C ++.

Одно определение размера выравнивания :

Размер выравнивания структуры – это смещение от одного элемента к следующему элементу, когда у вас есть массив этой структуры.

По своей природе, если у вас есть массив структуры с двумя элементами, то оба должны иметь выровненные элементы, поэтому это означает, что да, размер должен быть кратным выравниванию. (Я не уверен, что какой-либо стандарт явно применяет это, но поскольку размер и выравнивание структуры не зависят от того, является ли структура одной или внутри массива, те же правила применяются к обоим, поэтому она не может быть любым другим способом.)

5.3.3 / 2

При применении к classу результатом [sizeof] является количество байтов в объекте этого classа, включая любое дополнение, необходимое для размещения объектов этого типа в массиве.

Итак, да, размер объекта кратен его выравниванию.

В стандарте говорится (раздел [dcl.array] :

Объект типа массива содержит смежно выделенный непустой набор из N подобъектов типа T.

Поэтому между элементами массива нет прокладки.

Стандарт не требует заполнения внутренних структур, но стандарт не допускает другого способа выравнивания элементов массива.

Я не уверен, что это в самом стандарте C / C ++, и я склонен сказать, что это зависит от компилятора (просто чтобы быть в безопасности). Тем не менее, у меня было «веселое» время, выяснив это несколько месяцев назад, когда мне пришлось отправлять динамически сгенерированные C-структуры в виде массивов байтов по сети в рамках протокола для связи с чипом. Выравнивание и размер всех структур должны были соответствовать структурам кода, запущенного на чипе, который был скомпилирован с вариантом GCC для архитектуры MIPS. Я попытаюсь дать алгоритм, и он должен применяться ко всем вариантам gcc (и, мы надеемся, большинству других компиляторов).

Все базовые типы, такие как char , short и int, соответствуют их размеру, и они выравниваются со следующей доступной позицией независимо от выравнивания родителя . И чтобы ответить на исходный вопрос, да, общий размер кратен выравниванию.

 // size 8 struct { char A; //byte 0 char B; //byte 1 int C; //byte 4 }; 

Несмотря на то, что выравнивание структуры составляет 4 байта, символы все еще упаковываются как можно ближе.

Выравнивание структуры равно наибольшему выравниванию ее элементов .

Пример:

 //size 4, but alignment is 2! struct foo { char A; //byte 0 char B; //byte 1 short C; //byte 3 } //size 6 struct bar { char A; //byte 0 struct foo B; //byte 2 } 

Это также относится к профсоюзам и любопытным образом. Размер объединения может быть больше любого размера его членов, просто из-за выравнивания:

 //size 3, alignment 1 struct foo { char A; //byte 0 char B; //byte 1 char C; //byte 2 }; //size 2, alignment 2 struct bar { short A; //byte 0 }; //size 4! alignment 2 union foobar { struct foo A; struct bar B; } 

Используя эти простые правила, вы должны иметь возможность выяснить выравнивание / размер любого ужасно вложенного union / struct, с которым вы сталкиваетесь. Это все из памяти, поэтому, если я пропустил угловой случай, который не может быть решен из этих правил, пожалуйста, дайте мне знать!

Чтобы разделить вопрос на две части:

1. Является ли это законным?

[5.3.3.2] При применении к classу результат [оператора sizeof ()] – это количество байтов в объекте этого classа, включая любое дополнение, необходимое для размещения объектов этого типа в массиве.

Итак, нет, это не так.

2. Ну, почему?

Здесь, я знаю, только размышляю.

2.1. Арифметика указателя становится более странной
Если выравнивание будет «между элементами массива», но не повлияет на размер, zthigns будут бесполезно сложными, например

 (char *)(X+1) != ((char *)X) + sizeof(X) 

(У меня есть догадка, что это требуется неявно стандартом даже без инструкции выше, но я не могу доказать это)

2.2 Простота
Если выравнивание влияет на размер, выравнивание и размер можно решить, просмотрев один тип. Учти это:

 struct A { int x; char y; } struct B { A left, right; } 

В текущем стандарте мне просто нужно знать sizeof (A), чтобы определить размер и расположение B.
С альтернативой вы предлагаете мне знать внутренние example2 A. Подобно вашему example2 : для «лучшей упаковки» sizeof (пример) недостаточно, вам нужно рассмотреть внутренности примера.

C ++ явно не говорит об этом, но это является следствием двух других требований:

Во-первых, все объекты должны быть хорошо выровнены.

3.8 / 1 говорит

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

и 3.9 / 5:

Типы объектов имеют * требования к выравниванию (3.9.1, 3.9.2). Выравнивание полного типа объекта представляет собой целочисленное значение, определенное реализацией, представляющее собой количество байтов; объект выделяется по адресу, который соответствует требованиям к выравниванию его типа объекта.

Поэтому каждый объект должен быть выровнен в соответствии с его требованиями к выравниванию.

Другое требование состоит в том, что объекты в массиве распределены смежно:

8.3.4 / 1:

Объект типа массива содержит смежно выделенный непустой набор из N подобъектов типа T

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

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

Кажется, стандарт C ++ 03 не сказал (или я не нашел), должны ли быть включены байты выравнивания выравнивания в представлении объекта.

И в стандарте C99 говорится, что «sizeof» тип структуры или тип объединения включает в себя внутреннее и конечное заполнение, но я не уверен, что все дополнения выравнивания включены в эту «заднюю прописку».

Вернемся к вашему примеру. На самом деле нет путаницы. sizeof(example) == 8 означает, что структура занимает 8 байтов для представления самого себя, включая байты заполнения хвоста 3. Если символ во второй структуре имеет смещение 6, он перезапишет пространство, используемое m_Example . Макет определенного типа определяется реализацией и должен оставаться стабильным во всей реализации.

Тем не менее, является ли p+1 равно (T*)((char*)p + sizeof(T)) неуверенным. И я надеюсь найти ответ.

  • Копирование одной структуры в другую
  • Разница между 'struct' и 'typedef struct' в C ++?
  • Почему я не могу скопировать инициализацию структуры, полученной из другой структуры?
  • Структура массивов по сравнению с массивом структур в CUDA
  • Как включить динамический массив INSIDE a struct в C?
  • Почему GCC не оптимизирует структуры?
  • Есть ли способ перебрать структуру с элементами разных типов в C?
  • Как использовать строку C ++ в структуре, когда malloc () - с той же структурой?
  • Структура структуры памяти C
  • Структурный конструктор в C ++?
  • Какая польза для тегов в Go?
  • Давайте будем гением компьютера.