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

25
задан Hrvoje Prgeša 13 December 2008 в 17:28
поделиться

10 ответов

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

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

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

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

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

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

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

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

26
ответ дан Robert Gamble 15 October 2019 в 15:44
поделиться

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

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
ответ дан Andrew Top 15 October 2019 в 15:44
поделиться

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

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

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


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

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

6
ответ дан Michael Burr 15 October 2019 в 15:44
поделиться

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

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

3
ответ дан Dan Hewett 15 October 2019 в 15:44
поделиться

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

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

3
ответ дан Ryan 15 October 2019 в 15:44
поделиться

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

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

Затем выполненный:

dt somemodule!CSomeType
2
ответ дан Ana Betts 15 October 2019 в 15:44
поделиться

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

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

1
ответ дан gnud 15 October 2019 в 15:44
поделиться

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

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

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

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

1
ответ дан RandomNickName42 15 October 2019 в 15:44
поделиться

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

Выравнивание структуры 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
ответ дан carmin 15 October 2019 в 15:44
поделиться

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

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

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

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

0
ответ дан user1969104 15 October 2019 в 15:44
поделиться