Несоосность элементов в структурах

В C некоторые элементы структуры имеют тенденцию иметь смещенные смещения, как в случае этого потока в Сообщество HPUX

В таком случае предлагается использовать битовое поле нулевой ширины для выравнивания (смещенного) следующего элемента. При каких обстоятельствах происходит перекос элементов конструкции? Разве компилятор не должен выравнивать смещения членов по границе слова?


person Phalgun    schedule 14.05.2014    source источник
comment
Пожалуйста, укажите ваши источники. (один предложен? Кем?). Задача компилятора — правильно выровнять элементы по мере необходимости, и компиляторы с этим справляются хорошо. Таким образом, нет тенденции к несовмещенным смещениям.   -  person rici    schedule 15.05.2014
comment
@rici Ну, я ответил на вопрос в сообществе HPUX о дампе ядра, созданном из-за неправильного выравнивания члена структура. Вы можете видеть, что это действительно происходит, и людей это раздражает.   -  person Phalgun    schedule 15.05.2014
comment
@ZanLynx, я просмотрел эту ветку перед публикацией, и она не отвечает на мой вопрос, почему компиляторы не выравнивают только определенные элементы структуры.   -  person Phalgun    schedule 15.05.2014
comment
Компиляторы автоматически присваивают членам структуры любое необходимое им выравнивание. Код в потоке, на который вы ссылаетесь, возможно, неправильно предполагал более строгое выравнивание, чем на самом деле требуют участники.   -  person Keith Thompson    schedule 15.05.2014
comment
@Phalgun: Пожалуйста, отредактируйте свой вопрос, чтобы включить эту ссылку. В противном случае очень сложно понять, о чем вы говорите.   -  person rici    schedule 15.05.2014
comment
проголосовали за повторное открытие, потому что связанный вопрос касается битовых полей нулевой длины, тогда как этот вопрос касается обстоятельств, при которых члену структуры может потребоваться явно указанное выравнивание. ИМХО, связанный вопрос фактически является вопросом XY, а не ответом.   -  person rici    schedule 15.05.2014
comment
@ncl: я не смотрел, но подозреваю, что это просто меняет вопрос, дубликатом которого является этот вопрос.   -  person Zan Lynx    schedule 16.05.2014
comment
@ncl: о да ... есть 1205 результатов поиска для [c] выравнивания структуры. вероятно проект целой недели, чтобы очистить это.   -  person Zan Lynx    schedule 16.05.2014


Ответы (2)


«Смещение» элемента конструкции может произойти только в том случае, если требования к выравниванию элемента конструкции намеренно скрыты. (Или если для подавления выравнивания используется какой-то специфичный для реализации механизм, например атрибут packed в gcc.)

Например, в упомянутой проблеме проблема в том, что есть структура:

struct {
    // ... stuff
    int               val;
    unsigned char     data[DATA_SIZE];
    // ... more stuff
}

и программист пытается использовать data, как если бы это было size_t:

*(size_t*)s->data

Однако программист объявил data как unsigned char, и поэтому компилятор только гарантирует, что он выровнен для использования в качестве unsigned char.

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

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

Упомянутый поток предлагает вставить битовое поле нулевой длины size_t перед объявлением массива unsigned char, чтобы принудительно выровнять массив для size_t. Хотя это решение может работать на целевой архитектуре, оно не является переносимым и не должно использоваться в переносимом коде. Нет никакой гарантии, что битовое поле нулевой длины будет занимать 0 бит, а также нет никакой гарантии, что битовое поле, основанное на size_t, действительно будет сохранено в size_t или будет соответствующим образом выровнено для любого использования, не связанного с битовым полем.

Лучшим решением было бы использовать анонимный союз:

// ...
int             val;
union {
  size_t        dummy;
  unsigned char data[DATA_SIZE];
};
// ...

С C11 вы можете явно указать минимальное выравнивание:

// ...
int                            val;
_Alignas(size_t) unsigned char data[DATA_SIZE];
// ...

В этом случае, если вы #include <stdalign.h>, вы можете написать _Alignas таким образом, который будет работать и с C++11:

int                            val;
alignas(size_t) unsigned char data[DATA_SIZE];
person rici    schedule 14.05.2014
comment
Спасибо за ваше объяснение @rici. Тем не менее, я хотел бы добавить выдержку из стандарта C99. В качестве особого случая член структуры битового поля с шириной 0 указывает, что никакое другое битовое поле не должно быть упаковано в единицу, в которой предыдущее битовое поле , если таковые имеются, не указывает ли это на то, что битовые поля нулевой ширины должны быть переносимыми? - person Phalgun; 15.05.2014
comment
@Phalgun: Цитата говорит именно то, что говорит, и не более того. Прочтите предыдущий абзац: Реализация может выделять любую адресную единицу хранения, достаточно большую для хранения битового поля... Выравнивание адресуемой единицы памяти не указано. Таким образом, нет никаких гарантий относительно выравнивания или размера; только то, что два битовых поля, разделенные битовым полем нулевой длины, не находятся в одной и той же адресуемой единице хранения. Это не влияет на выравнивание следующего массива char, поскольку следующего битового поля нет, и даже если бы оно было, оно не имело бы определенного выравнивания. - person rici; 15.05.2014

В: Почему происходит смещение? Разве компилятор не должен выравнивать смещения членов по границе слова?

Вы, вероятно, знаете, что причина, по которой поля структуры выравниваются по определенным границам, заключается в повышении производительности. Правильно выровненное поле может потребовать от ЦП только одной операции выборки памяти; где неправильно выровненное поле потребует по крайней мере двух операций выборки памяти (удвоенное время процессора).

Как вы указали, работа компиляторов заключается в выравнивании полей структуры для быстрого доступа к ЦП; если только программист не переопределит поведение компилятора по умолчанию.

Тогда вопрос может быть; Зачем программисту переопределять выравнивание полей структуры компилятором по умолчанию?

Одним из примеров того, почему программист может захотеть переопределить выравнивание по умолчанию, является отправка структуры «по сети» на другой компьютер. Как правило, программист хочет упаковать как можно больше данных в наименьшее количество байтов.

Следовательно, программист отключит выравнивание по умолчанию, когда плотность структуры важнее, чем производительность ЦП при доступе к полям структуры.

person Mahonri Moriancumer    schedule 14.05.2014
comment
Стоит отметить, что не все процессоры будут страдать только от снижения производительности. Некоторые, например Sparc, будут создавать дамп ядра при невыровненном доступе. - person camelccc; 15.05.2014
comment
@camelccc, спасибо за разъяснение. - person Mahonri Moriancumer; 15.05.2014