Определение выравнивания структур 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
    • пока не стандартно, но близко (технический отчет 1), должно быть в C ++ 0x
    • в последнем черновике присутствуют следующие ограничения: Предварительное условие: T должен быть полным типом, ссылочным типом или массивом с неизвестной границей, но не должен быть типом функции или (возможно, cv-квалифицированным) недействительным.
      • это означает, что мой представленный вариант использования с гибким массивом C99 не будет работать (это не удивительно, поскольку гибкие массивы не являются стандартным c ++)
    • в последнем проекте c ++ это определено в терминах нового ключевого слова - alignas (это имеет то же самое полное требование типа)
    • по моему мнению, если стандарт c ++ когда-либо будет поддерживать гибкие массивы C99, требование может быть ослаблено (выравнивание структуры с гибким массивом не должно меняться в зависимости от количества элементов массива)
  • c ++ boost :: alignment_of
    • в основном замена tr1
    • кажется, специализируется на void и возвращает 0 в этом случае (это запрещено в проекте c ++)
    • Примечание от разработчиков: строго говоря, вы должны полагаться только на значение ALIGNOF (T), кратное истинному выравниванию T, хотя на практике оно вычисляет правильное значение во всех случаях, о которых мы знаем.
    • Я не знаю, работает ли это с гибкими массивами, это должно (может не работать вообще, это разрешает встроенную функцию компилятора на моей платформе, поэтому я не знаю, как это будет вести себя в общем случае)
  • Эндрю Топ представил простое шаблонное решение для расчета выравнивания в ответах
    • кажется, что это очень близко к тому, что делает boost (boost будет дополнительно возвращать размер объекта в качестве выравнивания, если оно меньше расчетного выравнивания, насколько я могу видеть), так что, вероятно, применимо то же самое уведомление
    • это работает с гибкими массивами
  • используйте Windbg.exe, чтобы узнать выравнивание символа
    • не время компиляции, специфично для компилятора, не проверял
  • использование offsetof для анонимной структуры, содержащей тип
    • увидеть ответы, не надежные, не переносимые с C ++ non-POD
  • встроенные функции компилятора, например MSVC __alignof
    • работает с гибкими массивами
    • Ключевое слово alignof в последней версии C ++

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

Как я вижу, есть только 1 решение - используйте старый взлом структуры:

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 на нем
  • последний проект c ++ содержит std :: align_storage
    • Значение default-alignment должно быть самым строгим требованием выравнивания для любого типа объекта C ++, размер которого не превышает Len
      • так что std::alignment_of< std::aligned_storage<BigEnoughNumber>>::valueдолжно дать нам максимальное выравнивание
      • только черновик, еще не стандартный (если вообще), tr1::aligned_storageне имеет этого свойства

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

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

12.12.2008 23:24:36
Суть в том, что только компилятор знает, как он будет выравнивать объекты во время компиляции, поэтому единственный способ получить эту информацию - от компилятора. Пока это не стандартизировано, вам нужно будет использовать расширения компилятора. Для чего вам нужна эта информация?
Robert Gamble 13.12.2008 17:39:57
Виртуальная машина (низкоуровневые конструкции, необходимые для объектов, массивов, сборщика мусора и многих других вещей.) Это студенческий проект, который должен быть как можно более «портативным» и «стандартным».
Hrvoje Prgeša 13.12.2008 20:51:00
Заставить это работать не является проблемой (и в основном уже сделано), но у меня нет времени для тестирования проекта на нескольких платформах и компиляторах, поэтому я искал некоторые стандартные совместимые решения проблем, с которыми я столкнулся.
Hrvoje Prgeša 13.12.2008 20:51:47
Я понимаю, что это трудно сделать в отношении стандартов. Я суммировал информацию, надеясь, что она будет полезна для других, ищущих те же вещи. (ограничение в 300 символов для комментариев смешно)
Hrvoje Prgeša 13.12.2008 20:52:38
Что касается стандартов, я также разочарован тем фактом, что в последнем проекте C почти нет упоминания о выравнивании - через несколько лет c ++ может стать более «низкоуровневым», чем C, благодаря его ключевым словам alignof и align :)
Hrvoje Prgeša 13.12.2008 20:53:40
10 ОТВЕТОВ
РЕШЕНИЕ

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

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

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

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

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

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

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

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

26
13.12.2008 02:44:30
Я верю, что ты прав. но мне трудно сделать логический вывод из трех фактов. не могли бы вы наметить основные шаги, пожалуйста?
Johannes Schaub - litb 12.12.2008 23:50:11
подожди, я думаю, я понял. если бы выравнивание структуры было менее строгим, тогда не было бы способа правильно заполнить элементы. выравнивание структуры должно быть кратным выравниванию выравнивания всех ее членов, верно?
Johannes Schaub - litb 12.12.2008 23:54:31
Я не уверен, что вы не следуете, я думаю, это примерно так же хорошо, как я могу выразиться. Вы принимаете 3 заявления, которые я сделал? Если это так, структура, которая не имеет выравнивания самого строгого члена, будет иметь элементы, которые не выровнены должным образом в массиве таких структур.
Robert Gamble 12.12.2008 23:55:22
Я думаю, что у вас есть это сейчас, есть способ, которым вы думаете, что я могу быть более ясным, пожалуйста, дайте мне знать, и я обновлю свой ответ.
Robert Gamble 12.12.2008 23:56:24
я думаю, твой ответ в порядке. получил это сейчас :)
Johannes Schaub - litb 12.12.2008 23:59:28

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

Я был бы очень рад оказаться неправым =)

1
12.12.2008 23:28:29
На самом деле стандарт дает несколько гарантий, в том числе: 1) нет заполнения перед первым членом структуры, 2) элементы структуры располагаются в памяти в порядке их определения, и 3) массивы не имеют заполнения между своими члены.
Robert Gamble 12.12.2008 23:33:18
Объедините эти гарантии с тем фактом, что все структуры могут использоваться в качестве элементов массива, и из этого следует, что выравнивание структуры должно быть по меньшей мере таким же строгим, как и выравнивание его наиболее строгого элемента.
Robert Gamble 12.12.2008 23:35:16
Я знаю, что мы говорим о C здесь, но для ясности, C ++ ослабляет требование, чтобы члены структуры были расположены в порядке, который они определены в определенных условиях.
Michael Burr 13.12.2008 07:50:28
@Mike B Истина, но правила C по-прежнему применяются, если тип POD.
Hrvoje Prgeša 13.12.2008 16:02:22

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

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

3
13.12.2008 00:22:45
Я думаю, что вы путаете выравнивание с отступом, это взаимосвязанные, но разные понятия. Обивка используется для обеспечения правильного выравнивания, что и делает прагма пакета; если вам нужно использовать явное заполнение в вашей структуре, то почти наверняка что-то не так с кодом, который обращается к нему.
Robert Gamble 12.12.2008 23:38:19
Если вы программируете встроенные устройства или двоичную связь, вы в конечном итоге сталкиваетесь с выравниванием и заполнением довольно часто. Хотя вопрос был о выравнивании, я просто хотел упомянуть отступы на случай, если это будет побочной проблемой.
Ryan 13.12.2008 00:11:11
Мне также любопытно ваше утверждение: «... если вам нужно использовать явное заполнение в вашей структуре ...» Вы программировали ebedded устройства и кросс-платформенный / компилятор? Вы видели код ядра Linux? Иногда это абсолютно необходимо.
Ryan 13.12.2008 00:16:33
@ Роберт Гэмбл: «с кодом почти наверняка что-то не так» Не обязательно. Если вы хотите, чтобы два элемента не отображались в одной и той же строке кэша, для предотвращения пинг-понга на многоядерном процессоре вам необходимо вручную добавить правильное количество заполнения между элементами.
paxos1977 13.12.2008 00:56:03

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

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

3
12.12.2008 23:36:02

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

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;
13
13.12.2008 00:29:56
Это должно быть эквивалентно std :: tr1 :: alignment_of <T> :: value, хотя это действительно чистая и простая реализация.
Hrvoje Prgeša 13.12.2008 00:57:16
Не знал о std :: tr1 :: alignment_of <T> ... Жаль, я тоже немного поискал и ничего не нашел. Спасибо за упоминание этого.
Andrew Top 13.12.2008 01:26:09

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

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

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


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

Некоторые компиляторы имеют расширение, позволяющее получить выравнивание типа (например, начиная с VS2002, MSVC имеет __alignof()встроенную функцию ). Они должны быть использованы при наличии.

6
13.12.2008 07:46:51
Это даст вам выравнивание, которое компилятор выберет для типа t внутри заданной структуры, но это может быть не выравнивание типа t, если оно отсутствует в такой структуре.
Robert Gamble 13.12.2008 03:00:54
Я допускаю, что стандарт может не гарантировать, что этот макрос будет работать, поэтому может быть целесообразно защитить его с помощью элементов управления препроцессором #if, чтобы он был правильно определен для конкретных компиляторов. Тем не менее, это, кажется, работает довольно хорошо в самом деле.
Michael Burr 13.12.2008 07:39:48
Кроме того, насколько я помню, offsetof гарантированно работает только для POD, поэтому, если t не POD, это снова выходит за рамки стандарта.
Hrvoje Prgeša 13.12.2008 12:51:13

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

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

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

dt somemodule!CSomeType
2
13.12.2008 01:57:44

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

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

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

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

1
21.02.2009 02:54:17

Модифицировано из блога Питера Джута

Выравнивание структуры 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

1
20.10.2012 17:55:38

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

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

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

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

0
18.01.2017 12:36:25
Я был бы действительно странным и несоответствующим, поскольку значения выравнивания всегда являются степенями 2: 6.2.8 Выравнивание объектов [...] 4 [...] Каждое действительное значение выравнивания должно быть неотрицательной интегральной степенью двойки.
chqrlie for yellow blockquotes 18.01.2017 10:43:32
Спасибо за то, что указали, что стандарт не позволяет значению выравнивания быть чем-либо иным, чем степень 2. Это добавлено в C11 или также присутствовало в C99? Я видел 80-битный float на некоторых платформах, и, конечно, его требование выравнивания могло все еще быть степенью 2.
user1969104 18.01.2017 11:17:14