Определение выравнивания структур C / C ++ по отношению к его членам

Можно ли найти выравнивание структурного типа, если известны выравнивания элементов конструкции?

Например. за:

struct S
{
 a_t a;
 b_t b;
 c_t c[];
};

выравнивание S = max (alignment_of (a), alignment_of (b), alignment_of (c))?

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


Отредактировано: Большое спасибо за все ответы, особенно Роберту Гэмблу, который дал действительно хороший ответ на исходный вопрос, и другим участникам.

Короче:

Чтобы обеспечить требования к выравниванию элементов конструкции, выравнивание конструкции должно быть не менее строгим, чем выравнивание самого строгого члена.

Что касается определения выравнивания структуры, было представлено несколько вариантов, и после небольшого исследования я обнаружил:

  • c++ std::tr1::alignment_of
    • not standard yet, but close (technical report 1), should be in the C++0x
    • the following restrictions are present in the latest draft: Precondition:T shall be a complete type, a reference type, or an array of unknown bound, but shall not be a function type or (possibly cv-qualified) void.
      • this means that my presented use case with the C99 flexible array won't work (this is not that surprising since flexible arrays are not standard c++)
    • в последнем проекте C ++ он определен в терминах нового ключевого слова - alignas (оно имеет такое же полное требование типа)
    • на мой взгляд, если стандарт С ++ когда-либо будет поддерживать гибкие массивы C99, требование можно было бы ослабить (выравнивание структуры с гибким массивом не должно изменяться в зависимости от количества элементов массива)
  • c++ boost::alignment_of
    • mostly a tr1 replacement
    • кажется специализированным для void и в этом случае возвращает 0 (это запрещено в черновике С ++)
    • Примечание разработчиков: строго говоря, вы должны полагаться только на то, что значение ALIGNOF (T) кратно истинному выравниванию T, хотя на практике оно вычисляет правильное значение во всех известных нам случаях.
    • Я не знаю, работает ли это с гибкими массивами, он должен (может не работать в целом, это разрешает компилятор, встроенный в мою платформу, поэтому я не знаю, как он будет себя вести в общем случае)
  • Andrew Top presented a simple template solution for calculating the alignment in the answers
    • this seems to be very close to what boost is doing (boost will additionally return the object size as the alignment if it is smaller than the calculated alignment as far as I can see) so probably the same notice applies
    • это работает с гибкими массивами
  • use Windbg.exe to find out the alignment of a symbol
    • not compile time, compiler specific, didn't test it
  • using offsetof on the anonymous structure containing the type
    • see the answers, not reliable, not portable with c++ non-POD
  • compiler intrinsics, eg. MSVC __alignof
    • works with flexible arrays
    • Ключевое слово alignof находится в последней версии проекта C ++

Если мы хотим использовать «стандартное» решение, мы ограничены std :: tr1 :: alignment_of, но это не сработает, если вы смешаете свой код c ++ с гибкими массивами c99.

На мой взгляд, есть только одно решение - использовать старую структуру:

struct S
{
 a_t a;
 b_t b;
 c_t c[1]; // "has" more than 1 member, strictly speaking this is undefined behavior in both c and c++ when used this way
};

В этом случае (и в любом другом случае) несовпадающие стандарты c и c ++ и их растущие различия вызывают сожаление.


Другой интересный вопрос (если мы не можем определить выравнивание структуры переносимым способом), каково самое строгое возможное требование выравнивания. Я могу найти несколько решений:

  • boost (внутренне) использует объединение множества типов и использует на нем boost :: alignment_of
  • the latest c++ draft contains std::aligned_storage
    • The value of default-alignment shall be the most stringent alignment requirement for any C++ object type whose size is no greater than Len
      • so the std::alignment_of< std::aligned_storage<BigEnoughNumber>>::value should give us the maximum alignment
      • только черновик, еще не стандартный (если когда-либо), tr1::aligned_storage не имеет этого свойства

Любые мысли по этому поводу также будут оценены.

Я временно снял отметку с принятого ответа, чтобы получить больше информации о новых подвопросах и внести в них свой вклад


person Hrvoje Prgeša    schedule 12.12.2008    source источник
comment
Суть в том, что только компилятор знает, как он будет выравнивать объекты во время компиляции, поэтому единственный способ получить эту информацию - от компилятора. Пока это не будет стандартизировано, вам нужно будет использовать расширения компилятора. Что вы делаете, для чего вам нужна эта информация?   -  person Robert Gamble    schedule 13.12.2008
comment
Виртуальная машина (низкоуровневые конструкции, необходимые для объектов, массивов, сборщика мусора и многих других вещей). Это студенческий проект, который должен быть максимально портативным и соответствовать стандартам.   -  person Hrvoje Prgeša    schedule 13.12.2008
comment
Заставить это работать - не проблема (и в основном это уже сделано), но у меня нет времени тестировать проект на нескольких платформах и компиляторах, поэтому я искал некоторые стандартные совместимые решения проблем, с которыми я столкнулся.   -  person Hrvoje Prgeša    schedule 13.12.2008
comment
Я понимаю, что это сложно сделать в отношении стандартов. Я обобщил информацию, надеясь, что она будет полезна другим, ищущим то же самое. (ограничение в 300 символов в комментариях просто смешно)   -  person Hrvoje Prgeša    schedule 13.12.2008
comment
Что касается стандартов, я также разочарован тем фактом, что в последнем проекте C почти нет упоминания о выравнивании - через несколько лет C ++ может стать более низкоуровневым, чем C с его ключевыми словами alignof и align :)   -  person Hrvoje Prgeša    schedule 13.12.2008


Ответы (10)


Здесь есть два тесно связанных понятия:

  1. Выравнивание, необходимое процессору для доступа к определенному объекту
  2. Выравнивание, которое компилятор фактически использует для размещения объектов в памяти.

Чтобы обеспечить требования к выравниванию элементов конструкции, выравнивание конструкции должно быть по крайней мере таким же строгим, как выравнивание ее самого строгого члена. Я не думаю, что это четко прописано в стандарте, но это можно сделать вывод из следующих фактов (которые отдельно изложены в стандарте):

  • Структуры могут иметь отступы между членами (и в конце).
  • Массивы не могут иметь отступы между элементами.
  • Вы можете создать массив любого типа структуры

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

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

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

Также нет переносимого способа (по крайней мере, в C) для определения того, как компилятор на самом деле будет выравнивать объект, хотя многие компиляторы имеют опции для обеспечения некоторого уровня контроля над выравниванием.

person Robert Gamble    schedule 12.12.2008
comment
я считаю, что ты прав. но мне трудно сделать логический вывод из трех фактов. не могли бы вы наметить основные шаги? - person Johannes Schaub - litb; 13.12.2008
comment
подожди, я думаю, я понял. если бы выравнивание структуры было менее строгим, то не было бы способа правильно заполнить элементы. выравнивание структуры должно быть кратным выравниванию всех ее членов, верно? - person Johannes Schaub - litb; 13.12.2008
comment
Я не уверен, что вы не понимаете, я думаю, что это примерно так, как я могу это выразить. Вы согласны с тремя заявлениями, которые я сделал? Если это так, структура, не имеющая выравнивания самого строгого члена, будет иметь элементы, которые не выровнены должным образом в массиве таких структур. - person Robert Gamble; 13.12.2008
comment
Я думаю, что теперь у вас есть это, есть способ, которым, по вашему мнению, я могу быть более ясным, пожалуйста, дайте мне знать, и я обновлю свой ответ. - person Robert Gamble; 13.12.2008
comment
Я думаю, твой ответ в порядке. получил это сейчас :) - person Johannes Schaub - litb; 13.12.2008
comment
Что касается определения выравнивания, как насчет c ++ std :: tr1 :: alignment_of ‹S› :: value? - person Hrvoje Prgeša; 13.12.2008
comment
Я не знаю о alignment_of (я программист на C, плохо разбираюсь в C ++), это стандарт? Можете дать ссылку? - person Robert Gamble; 13.12.2008
comment
Насчет второго пункта я не уверен. Предположим, у вас есть структура S {int a; char b; } в 32-битной архитектуре, которая требует привязки целых чисел к 4-байтовой границе. Конечно, массив S будет иметь отступ между двумя элементами, не так ли? - person David Rodríguez - dribeas; 13.12.2008
comment
@dribeas: Нет, отступы должны быть частью структуры. Вы можете попробовать это сами, распечатав значение sizeof (struct S). - person Robert Gamble; 13.12.2008
comment
@Robert Gamble alignment_of стандартный (точнее технический отчет 1). Ссылка MSDN: msdn.microsoft.com/en-us/library/bb982233.aspx - person Hrvoje Prgeša; 13.12.2008
comment
Выравнивание структуры должно быть не меньше, чем выравнивание каждого из ее членов - по крайней мере, часть все еще меня беспокоит - почему она может быть больше? - person Hrvoje Prgeša; 13.12.2008
comment
@Hrvoje: Я не могу придумать ни одной причины, по которой выравнивание структуры было бы больше, чем у любого из ее членов, хотя компилятор мог вставить столько отступов, сколько захочет, до конца. Я обновлю свой ответ после того, как подумаю об этом еще немного ... - person Robert Gamble; 13.12.2008
comment
разве это не очевидно? структура как {char a; char b; }; ‹< выровнять по 4 байта для лучшей скорости? в любом случае tr1 не является стандартом C ++. - person Johannes Schaub - litb; 13.12.2008

Я написал этот код признака типа для определения выравнивания любого типа (на основе уже обсужденных правил компилятора). Вы можете найти это полезным:

template <class T>
class Traits
{
public:
    struct AlignmentFinder
    {
        char a; 
        T b;
    };

    enum {AlignmentOf = sizeof(AlignmentFinder) - sizeof(T)};
};

Итак, теперь вы можете:

std::cout << "The alignment of structure S is: " << Traits<S>::AlignmentOf << std::endl;
person Andrew Top    schedule 13.12.2008
comment
Это должно быть эквивалентно std :: tr1 :: alignment_of ‹T› :: value, хотя это действительно чистая и простая реализация. - person Hrvoje Prgeša; 13.12.2008
comment
Не знал о std :: tr1 :: alignment_of ‹T› ... Жаль, я тоже немного поискал и ничего не нашел. Спасибо, что упомянули об этом. - person Andrew Top; 13.12.2008

Следующий макрос вернет требование выравнивания любого заданного типа (даже если это структура):

#define TYPE_ALIGNMENT( t ) offsetof( struct { char x; t test; }, test )

Примечание: я, вероятно, позаимствовал эту идею из заголовка Microsoft в какой-то момент в прошлом ...


Изменить: как указывает Роберт Гэмбл в комментариях, работа этого макроса не гарантируется. Фактически, это определенно не будет работать очень хорошо, если компилятор настроен на упаковку элементов в структуры. Поэтому, если вы решили использовать его, используйте его с осторожностью.

Некоторые компиляторы имеют расширение, позволяющее получить выравнивание типа (например, начиная с VS2002, MSVC имеет внутреннюю __alignof()). Их следует использовать, когда они доступны.

person Michael Burr    schedule 13.12.2008
comment
Это даст вам выравнивание, которое компилятор выберет для использования типа t в данной структуре, но это может быть не выравнивание типа t, если он не появляется в такой структуре. - person Robert Gamble; 13.12.2008
comment
Я согласен с тем, что стандарт не может гарантировать, что этот макрос будет работать, поэтому может быть целесообразно защитить его с помощью элементов управления препроцессором #if, чтобы он был правильно определен для определенных компиляторов. Тем не менее, похоже, что он работает довольно хорошо в цене. - person Michael Burr; 13.12.2008
comment
Кроме того, насколько я помню, offsetof гарантированно работает только для POD, поэтому, если t не является POD, это снова выходит за рамки стандарта. - person Hrvoje Prgeša; 13.12.2008

Как упоминали другие, его реализация зависит. Visual Studio 2005 использует 8 байтов в качестве выравнивания структуры по умолчанию. Внутри элементы выровнены по размеру - float имеет 4-байтовое выравнивание, double использует 8 и т. Д.

Вы можете изменить поведение с помощью #pragma pack. GCC (и большинство компиляторов) имеют аналогичные параметры компилятора или прагмы.

person Dan Hewett    schedule 12.12.2008

Можно предположить выравнивание структуры, если вы знаете более подробную информацию об используемых параметрах компилятора. Например, #pragma pack (1) приведет к принудительному выравниванию на уровне байтов для некоторых компиляторов.

Боковое примечание: я знаю, что вопрос был о выравнивании, но побочная проблема - это заполнение. Для встроенного программирования, двоичных данных и т. Д. - В общем, не предполагайте ничего о выравнивании структуры, если это возможно. При необходимости используйте в структурах явное заполнение. У меня были случаи, когда было невозможно скопировать точное выравнивание, используемое в одном компиляторе, в компилятор на другой платформе без добавления элементов заполнения. Это было связано с выравниванием структур внутри структур, поэтому добавление элементов padding исправило это.

person Ryan    schedule 12.12.2008
comment
Я думаю, вы путаете выравнивание с отступом, это связанные, но разные концепции. Padding используется для обеспечения правильного выравнивания, что и обрабатывает прагма pack; если вам нужно использовать явное заполнение в своей структуре, почти наверняка что-то не так с кодом, который обращается к нему. - person Robert Gamble; 13.12.2008
comment
Если вы программируете встроенные устройства или двоичную связь, вы довольно часто будете иметь дело с выравниванием и заполнением. Хотя вопрос был о выравнивании, я просто хотел упомянуть отступы на случай, если это была побочная проблема. - person Ryan; 13.12.2008
comment
Мне также интересно ваше утверждение, ... если вам нужно использовать явное заполнение в своей структуре ... Вы программировали ebedded устройства и кроссплатформенность / компилятор? Вы видели код ядра Linux? Иногда это абсолютно необходимо. - person Ryan; 13.12.2008
comment
@Robert Gamble: почти наверняка что-то не так с кодом Не обязательно. Если вы хотите, чтобы два элемента не отображались в одной строке кэша, чтобы предотвратить «пинг-понг» на многоядерном процессоре, вам необходимо вручную добавить правильное количество отступов между элементами. - person paxos1977; 13.12.2008

Если вы хотите выяснить это для конкретного случая в Windows, откройте windbg:

Windbg.exe -z \path\to\somemodule.dll -y \path\to\symbols

Затем запустите:

dt somemodule!CSomeType
person Ana Betts    schedule 13.12.2008

Я не думаю, что макет памяти каким-либо образом гарантирован каким-либо стандартом C. Это очень сильно зависит от производителя и архитектора. Могут быть способы сделать это, которые работают в 90% случаев, но они нестандартны.

Я был бы очень рад, если бы ошибся =)

person gnud    schedule 12.12.2008
comment
На самом деле стандарт дает несколько гарантий, включая: 1) нет заполнения перед первым членом структуры, 2) элементы структуры располагаются в памяти в том порядке, в котором они определены, и 3) массивы не имеют заполнения между своими члены. - person Robert Gamble; 13.12.2008
comment
Объедините эти гарантии с тем фактом, что все структуры могут использоваться как элементы массива, и из этого следует, что выравнивание структуры должно быть по крайней мере таким же строгим, как выравнивание ее самого строгого члена. - person Robert Gamble; 13.12.2008
comment
Я знаю, что мы говорим здесь о C, но для ясности: C ++ ослабляет требование, чтобы члены структуры располагались в том порядке, в котором они определены в определенных условиях. - person Michael Burr; 13.12.2008
comment
@Mike B Верно, но правила C по-прежнему применяются, если тип - POD. - person Hrvoje Prgeša; 13.12.2008

Я в основном согласен с Полом Беттсом, Райаном и Дэном. На самом деле, это зависит от разработчика, вы можете либо сохранить симанику выравнивания по умолчанию, о которой заметил Роберт (объяснение Роберта - это просто поведение по умолчанию и никоим образом не принудительно или не требуется), либо вы можете настроить любое выравнивание, которое хотите / Zp [# #].

Это означает, что если у вас есть typedef с числами float, long double, uchar и т. Д. ... включены различные наборы массивов. Затем есть другой тип, который имеет некоторые из этих элементов странной формы и один байт, затем еще один нечетный член, он будет просто выровнен с любым предпочтением, определенным файлом make / solution.

Как отмечалось ранее, используя команду windbg dt во время выполнения, вы можете узнать, как компилятор размещает структуру в памяти.

Вы также можете использовать любой инструмент для чтения pdb, например dia2dump, для статического извлечения этой информации из pdb.

person RandomNickName42    schedule 21.02.2009

Изменено из блога Пеэтера Джута

Выравнивание структуры C основано на собственном типе самого большого размера в структуре, по крайней мере, обычно (исключением является что-то вроде использования 64-битного целого числа в win32, где требуется только 32-битное выравнивание).

Если у вас есть только символы и массивы символов, после добавления int этот int будет начинаться на 4-байтовой границе (с возможным скрытым заполнением перед элементом int). Кроме того, если структура не кратна sizeof (int), в конце будет добавлено скрытое заполнение. То же самое для коротких и 64-битных типов.

Пример:

struct blah1 {
    char x ;
    char y[2] ;
};

sizeof (blah1) == 3

struct blah1plusShort {
    char x ;
    char y[2] ;
    // <<< hidden one byte inserted by the compiler here
    // <<< z will start on a 2 byte boundary (if beginning of struct is aligned).
    short z ;
    char w ;
    // <<< hidden one byte tail pad inserted by the compiler.
    // <<< the total struct size is a multiple of the biggest element.
    // <<< This ensures alignment if used in an array.
};

sizeof (blah1plusShort) == 8

person carmin    schedule 20.10.2012

Я прочитал этот ответ через 8 лет и считаю, что принятый ответ от @Robert в целом правильный, но математически неверный.

Чтобы обеспечить требования к выравниванию элементов конструкции, выравнивание конструкции должно быть не менее строгим, чем наименьшее общее кратное выравнивания ее элементов. Рассмотрим странный пример, где требования к выравниванию элементов равны 4 и 10; в этом случае выравнивание структуры - это LCM (4, 10), которое равно 20, а не 10. Конечно, странно видеть платформы с таким требованием выравнивания, которое не является степенью 2, и, следовательно, для всех практических случаев , выравнивание структуры равно максимальному выравниванию ее элементов.

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

Обновление: как указано @chqrlie в комментарии, стандарт C не допускает нечетных значений выравнивания. Однако этот ответ по-прежнему доказывает, почему выравнивание структуры является максимумом выравнивания ее элементов, просто потому, что максимум оказывается наименьшим общим кратным, и, таким образом, элементы всегда выровнены относительно общего кратного адреса.

person user1969104    schedule 18.01.2017
comment
Я был бы действительно странным и несоответствующим, поскольку значения выравнивания всегда являются степенями 2: 6.2.8 Выравнивание объектов [...] 4 [.. .] Каждое допустимое значение выравнивания должно быть неотрицательной целой степенью двойки. - person chqrlie; 18.01.2017
comment
Спасибо за указание на то, что стандарт не допускает, чтобы значение выравнивания могло быть чем-либо, кроме степени 2. Это добавлено в C11 или также присутствовало в C99? Я видел 80-битные числа с плавающей запятой на некоторых платформах, и, конечно, его требование выравнивания могло быть степенью двойки. - person user1969104; 18.01.2017