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

Общая начальная последовательность и выравнивание

Придумав встречный пример для этого вопроса, я придумал:

struct A
{
    alignas(2) char byte;
};

Но если этот законный и стандартный макет, совместим ли он с этим struct B?

struct B
{
    char byte;
};

Кроме того, если мы имеем

struct A
{
    alignas(2) char x;
    alignas(4) char y;
};
// possible alignment, - is padding
// 00 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15
//  x  -  -  -  y  -  -  -  x  -  -  -  y  -  -  -

struct B
{
    char x;
    char y;
}; // no padding required

union U
{
    A a;
    B b;
} u;

Существует ли общая начальная последовательность для A и B? Если да, включает ли он A::y и B::y? I.e., мы можем написать следующее w/o, вызывающее UB?

u.a.y = 42;
std::cout << u.b.y;

(ответы для С++ 1y/ "fixed С++ 11" также приветствуются)


  • См. [basic.align] для выравнивания и [dcl.align] для спецификатора выравнивания.

  • [basic.types]/11 говорит для фундаментальных типов "Если два типа T1 и T2 являются одним и тем же типом, то T1 и T2 являются совместимыми с макетами типами." (основной вопрос заключается в том, имеют ли A::byte и B::byte типы совместимости с макетами)

  • [class.mem]/16 "Два типа структуры стандартного макета совместимы с макетами, если они имеют одинаковое количество нестатических элементов данных и соответствующих нестатических членов данных (в порядке объявления) имеют макет -собираемые типы."

  • [class.mem]/18 "Две структуры стандартного компоновки имеют общую начальную последовательность, если соответствующие члены имеют совместимые с макетами типы, и ни один из них не является битовым полем или оба являются битовыми полями с одинаковой шириной для последовательности одного или нескольких начальных элементов. "

  • [class.mem]/18 "Если объединение стандартного макета содержит две или более структуры стандартного макета, которые имеют общую начальную последовательность, и если объект объединения стандартного макета в настоящее время содержит одну из этих структур стандартной компоновки, это разрешено для проверки общей исходной части любого из них ".

Конечно, на уровне юрисконсульта другой вопрос заключается в том, что это означает, что проверка общей исходной последовательности "разрешена". Я предполагаю, что какой-то другой абзац мог бы сделать описанное выше поведение u.b.x undefined (чтение из неинициализированного объекта).

4b9b3361

Ответ 1

Я не могу говорить о стандарте С++ 11, но я программист микропрограммного обеспечения/микрочипа и должен использовать такие функции, которые существуют в течение длительного времени (прагма-пакет, атрибуты выравнивания).

Использование alignas не может считаться "стандартным макетом", поэтому все последствия бесполезны. Стандартная компоновка означает одно фиксированное распределение выравнивания (для каждой архитектуры - обычно все align(min(sizeof,4)) или некоторые могут быть align(8)). Стандарт, вероятно, хочет сказать, что очевидно: без использования специальных функций (align, pack) структуры совместимы с одинаковой архитектурой, если они apepar одинаковы (одни и те же типы в одном порядке). В противном случае они могут и не могут быть совместимы - в зависимости от архитектуры (могут быть совместимы по одной архитектуре, но по другому).

Рассмотрим эту структуру:

struct foo{ char b; short h; double d; int i; };

В одной архитектуре (например, x86 32bit) это похоже на то, что кажется, но на Itanium или ARM это выглядит примерно так:

struct foo{char b, **_hidden_b**; short h; **int _maybe_hidden_h**; double d; int i;}  

Примечание _maybe_hidden_h - его можно опустить в более раннем AEABI (выровнять до максимального 4) или там для выравнивания 64 бит /8B.

x86 Стандартный макет (пакет (1)):

alignas(1) char b; alignas(1) short h; alignas(1) double d; alignas(1) int i;  

32-битная стандартная компоновка выравнивания (пакет (4) - архитектура ARM, более ранняя версия - EABI)

alignas(1) char b; alignas(2) short h; **alignas(4) double d**; alignas(4) int i;  

64-битная стандартная компоновка выравнивания (пакет (8) - Itanium и более новый ARM/AEABI)

alignas(1) char b; alignas(2) short h; **alignas(8) double d**; alignas(4) int i;

К вашему примеру:
offsetof(A,y) = 4 в то время как offsetof(B,y) = 2 и объединение не меняет этого (таким образом, &u.a.y != u.b.y)

Ответ 2

Похоже на отверстие в стандарте. Ответственная вещь заключалась бы в записи отчета .

Несколько вещей:

  • Ваш первый пример на самом деле не демонстрирует проблемы. Добавление short после char также привело бы к выравниванию char к 2-байтовой границе без изменения общей подпоследовательности.
  • alignas не только С++; он был добавлен одновременно C11. Поскольку свойство стандартного макета является средством совместимости между языками, вероятно, предпочтительнее требовать соответствия соответствующих спецификаторов выравнивания, чем дисквалификация класса с нестатистическим спецификатором выравнивания участника.
  • Не было бы проблем, если бы спецификаторы выравнивания элементов соответствовали типам членов. Другие проблемы могут возникнуть из-за отсутствия корректировки типов, например, может потребоваться изменить параметр функции ret fn( alignas(4) char ) для того, чтобы ABI обработал его правильно, но язык может не обеспечить такую ​​настройку.

Ответ 3

(основной вопрос заключается в том, имеют ли байты A:: байта и B::) совместимые типы)

Да. Это неотъемлемая часть. Атрибут alignas относится к объявлению , объявленному, а не типу. Может быть легко протестирован с помощью std::is_same и decltype.

I.e., мы можем написать следующее w/o, вызывающее UB?

Таким образом, это не UB, соответствующие параграфы были указаны вами.

EDIT: Простите меня, это, конечно, может привести к UB, потому что отступы между членами не определены (или реализованы) (§9.2/13)! Я случайно неправильно читаю пример, потому что я думал, что он обратился к x вместо y, потому что с x он всегда работает всегда, тогда как с y теоретически он не должен (хотя он практически всегда будет).