常见的初始序列和对齐

时间:2014-02-01 15:33:10

标签: c++ c++11 unions memory-alignment standard-layout

在考虑this question的反例时,我提出了:

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;

AB是否有共同的初始序列?如果是这样,它是否包含A::y& B::y?即,我们可以编写以下内容而无需调用UB吗?

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

(C ++ 1y /“fixed C ++ 11”的答案也欢迎)


  • 参见[basic.align]进行对齐,[dcl.align]作为alignment-specifier。

  • [basic.types] / 11表示基本类型“如果两种类型T1T2属于同一类型,则T1T2为布局兼容类型。“ (一个基本问题是A::byteB::byte是否具有布局兼容类型)

  • [class.mem] / 16“如果两个标准布局结构类型具有相同数量的非静态数据成员且相应的非静态数据成员(按声明顺序)具有布局,则它们是布局兼容的兼容的类型。“

  • [class.mem] / 18“两个标准布局结构共享一个共同的首字母 序列,如果相应的成员具有布局兼容类型,并且成员都不是位字段或 对于一个或多个初始成员的序列,两者都是具有相同宽度的位字段。“

  • [class.mem] / 18“如果标准布局联合包含两个或多个共享一个公共初始序列的标准布局结构, 如果标准布局联合对象当前包含这些标准布局结构之一,则允许 检查其中任何一个的共同初始部分。“

当然,在语言 - 律师层面,另一个问题是,对于共同的初始序列的检查是“允许的”意味着什么。我猜其他段落可能会导致上述u.b.x未定义的行为(从未初始化的对象中读取)。

3 个答案:

答案 0 :(得分:2)

我可能不会代表C ++ 11标准,但我是固件/微芯片程序员,必须使用长时间存在的这些功能(编译指示包,对齐属性)。

使用alignas不能被视为“标准布局”,因此所有含义都是无用的。标准布局意味着一个固定的对齐分布(每个架构 - 通常都是align(min(sizeof,4))或某些可能是align(8))。标准可能想要说明显而易见的事情:不使用特殊功能(alignpack)结构在相同的架构上是兼容的,如果它们相同(相同的顺序相同)。否则,它们可能也可能不兼容 - 取决于架构(可能在一个架构上兼容但在另一个架构上不同)。

考虑这个结构:

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

在一个架构上(例如x86 32位)它似乎是它,但在Itanium或ARM上它实际上是这样的:

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

注意_maybe_hidden_h - 在较早的AEABI(对齐最大4)或64bit / 8B对齐时可以省略它。

x86标准版面(pack(1)):

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

32位对齐标准布局(pack(4) - ARM体系结构,旧版本 - EABI)

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

64位对齐标准布局(pack(8) - Itanium和更新的ARM / AEABI)

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

以你的例子为例:
offsetof(A,y) = 4offsetof(B,y) = 2和联盟不会改变(&u.a.y != u.b.y

答案 1 :(得分:2)

它看起来像标准中的一个洞。负责任的是提交defect report

但有几件事情:

  • 你的第一个例子并没有真正证明问题。在short之后添加char也会使char与2字节边界对齐,而不会更改公共子序列。
  • alignas不仅仅是C ++;它被同时添加到C11。由于标准布局属性是一种跨语言兼容性工具,因此最好要求使用相应的对齐说明符进行匹配,而不是使用非静态成员对齐说明符取消对类的限定。
  • 如果成员对齐说明符属于成员类型,则没有问题。其他问题可能是由于缺乏对类型的调整,例如,函数参数ret fn( alignas(4) char )可能需要被修改以便ABI正确处理它,但语言可能无法提供此类调整。

答案 2 :(得分:0)

  

(一个基本问题是A :: byte和B :: byte是否具有布局兼容类型)

是。这是必不可少的部分。 alignas - 属性属于声明的实体,而不是类型。可以通过std::is_samedecltype轻松测试。

  

即,我们可以编写以下内容而无需调用UB吗?

因此,这不是UB,你引用了相关的段落。

编辑:请原谅,这当然可以导致UB,因为成员之间的填充不是(或实现)定义的(§9.2/ 13)!我意外地误读了这个例子,因为我认为它访问了x而不是y,因为使用x它实际上总是有效 - 而使用y它理论上不需要(尽管它实际上总是会这样)。