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

Думая о контр-примере для этот вопрос, Я придумал:

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? Т.е. можем ли мы написать следующее без вызова UB?

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

(ответы на C ++ 1y / «исправлено C ++ 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 неопределенное поведение (чтение из неинициализированного объекта).

13

Решение

Похоже на дыру в стандарте. Ответственным было бы подать отчет о дефектах.

Несколько вещей, хотя:

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

Другие решения

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

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

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

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

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

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

Т.е. можем ли мы написать следующее без вызова UB?

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

РЕДАКТИРОВАТЬ: Извините, это, конечно, может привести к UB, потому что заполнение между членами не определено (или реализация) (§9.2 / 13)! Я случайно неправильно прочитал пример, потому что я думал, что он получил доступ к x вместо y, потому что с x он на самом деле всегда работает — тогда как с y это теоретически не должно (хотя это практически всегда будет).

0
По вопросам рекламы [email protected]