Требуется ли, чтобы размер структуры был точным кратным выравниванию этой структуры? - PullRequest
17 голосов
/ 09 января 2011

Еще раз, я ставлю под сомнение давнюю веру.

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

struct example
{
  int   m_Assume_32_Bits;
  char  m_Assume_8_Bit_Bytes;
};

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

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

struct example2
{
  example m_Example;
  char    m_Why_Cant_This_Be_At_Offset_6_Bytes;
};

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

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

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

Итак - требуется ли размер структуры для точного умножения выравнивания этой структуры в соответствии со стандартом C ++?

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

Ответы [ 8 ]

17 голосов
/ 09 января 2011

5.3.3 / 2

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

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

9 голосов
/ 09 января 2011

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

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

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

6 голосов
/ 09 января 2011

Стандарт гласит (раздел [dcl.array]:

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

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

Заполнение внутри структур не требуется стандартом, но стандарт не разрешает какой-либо другой способ выравнивания элементов массива.

2 голосов
/ 09 января 2011

Я не уверен, соответствует ли это фактическому стандарту 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 байта, символы по-прежнему упакованы как можно ближе.

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

* * Пример тысяча двадцать-один: * * 1 022
//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;
}

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

1 голос
/ 09 января 2011

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

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

3.8/ 1 говорит

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

и 3.9 / 5:

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

Таким образом, каждый объект должен быть выровнен в соответствии с его требованиями выравнивания.

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

8.3.4 / 1:

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

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

1 голос
/ 09 января 2011

Итак, чтобы разделить ваш вопрос на две части:

1. Это законно?

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

Так что нет, это не так.

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.
С альтернативой, которую вы предлагаете, мне нужно знать внутренности A. Подобно вашему example2: для «лучшей упаковки» sizeof (пример) недостаточно, вы должны рассмотреть пример внутренних.

0 голосов
/ 09 января 2011

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

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

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

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

0 голосов
/ 09 января 2011

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

...