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

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

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

Eg. для:

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

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

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


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

Короче:

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

Что касается определения выравнивания структуры, было представлено несколько вариантов и с небольшим количеством исследований это то, что я нашел:

  • С++ std:: tr1:: alignment_of
    • пока не стандартный, но закрыть (технический отчет 1), должен быть в С++ 0x
    • в последнем проекте представлены следующие ограничения: Предварительное условие: T должен быть полным типом, ссылочным типом или массивом неизвестная граница, но не должна быть функциональным типом или (возможно, cv-qualified) void.
      • это означает, что мой представленный прецедент с гибким массивом C99 не будет работать (это не удивительно, поскольку гибкие массивы не являются стандартными С++).
    • в последнем проекте С++ определяется в терминах нового ключевого слова - alignas (у этого есть то же полное требование типа)
    • на мой взгляд, должен ли стандарт С++ поддерживать гибкие массивы C99, требование может быть ослаблено (выравнивание структуры с гибким массивом не должно меняться в зависимости от количества элементов массива)
  • С++ boost:: alignment_of
    • в основном замена tr1
    • кажется специализированным для void и возвращает 0 в этом случае (это запрещено в черновик С++)
    • Примечание разработчиков: строго говоря, вы должны полагаться только на значение ALIGNOF (T), кратное истинному выравниванию T, хотя на практике оно вычисляет правильное значение во всех случаях, о которых мы знаем.
    • Я не знаю, работает ли это с гибкими массивами, он должен (возможно, не работать вообще, это разрешает компилятор, основанный на моей платформе, поэтому я не знаю, как он будет вести себя в общем случае)
  • Andrew Top представил простое шаблонное решение для расчета выравнивания в ответах
    • это кажется очень близким к тому, что делает boost (boost будет дополнительно возвращать размер объекта в качестве выравнивания, если он меньше расчетного выравнивания, насколько я могу видеть), поэтому, возможно, такое же уведомление применяется
    • это работает с гибкими массивами
  • используйте Windbg.exe, чтобы узнать выравнивание символа
    • не компилировать время, компилятор специфичен, не тестировал его
  • с использованием offsetof в анонимной структуре, содержащей тип
    • см. ответы, не надежные, не портативные с С++ non-POD
  • встроенные функции компилятора, например. MSVC __alignof
    • работает с гибкими массивами.
    • ключевое слово alignof находится в последней версии С++

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

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

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 и С++ и их растущие различия являются неудачными в этом случае (и в каждом другом случае).


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

  • boost (внутренне) использует объединение различных типов и использует boost:: alignment_of на нем
  • последний проект С++ содержит std:: aligned_storage
    • Значение выравнивания по умолчанию должно быть самым строгим требованием выравнивания для любого типа объекта С++, размер которого не больше, чем Len
      • поэтому std::alignment_of< std::aligned_storage<BigEnoughNumber>>::value должен дать нам максимальное выравнивание
      • только черновик (если он когда-либо), tr1::aligned_storage не имеет этого свойства

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

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

4b9b3361

Ответ 1

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

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

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

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

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

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

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

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

Ответ 2

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

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;

Ответ 3

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

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

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


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

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

Ответ 4

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

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

Ответ 5

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

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

Ответ 6

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

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

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

dt somemodule!CSomeType

Ответ 7

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

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

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

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

Ответ 8

Изменено из Блог Peeter Joot

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

Ответ 9

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

Я был бы очень рад оказаться ошибочным, хотя =)

Ответ 10

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

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

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

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