Подтвердить что ты не робот

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

Еще раз, я задаюсь вопросом давней веры.

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

struct example
{
  int   m_Assume_32_Bits;
  char  m_Assume_8_Bit_Bytes;
};

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

Несколько исследований показали, что размер типа 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 предоставляет разные гарантии, меня это тоже интересует, но основное внимание уделяется С++.

4b9b3361

Ответ 1

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

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

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

Ответ 2

5.3.3/2

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

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

Ответ 3

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

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

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

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

Ответ 4

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

Все базовые типы, такие как char, короткие и 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, с которым вы сталкиваетесь. Это все из памяти, поэтому, если я пропустил угловой случай, который не может быть решен из этих правил, пожалуйста, дайте мне знать!

Ответ 5

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

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 (пример) недостаточно, вам нужно рассмотреть внутренние элементы примера.

Ответ 6

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

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

3.8/1 говорит

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

и 3.9/5:

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

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

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

8.3.4/1:

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

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

Ответ 7

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

Ответ 8

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

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

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

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