14

在考虑这个问题的反例时,我想出了:

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

但是,如果这是合法且标准的布局,那么它的布局是否与 this 兼容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 表示基本类型“如果两个类型T1T2是相同类型,那么T1T2是布局兼容类型。” (一个潜在的问题是是否A::byte并且B::byte具有布局兼容的类型)

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

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

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

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

4

3 回答 3

2

我可能不会代表 C++11 标准,但我是一名固件/微芯片程序员,并且不得不使用存在已久的此类功能(pragma pack、对齐属性)。

使用alignas不能被认为是“标准布局”,因此所有的含义都是无用的。标准布局意味着一个固定的对齐分布(每个架构 - 通常全部是align(min(sizeof,4))或一些可能是align(8))。该标准可能想说什么是显而易见的:不使用特殊功能(align, pack)结构在相同架构上是兼容的,如果它们看起来相同(相同的类型以相同的顺序)。否则,它们可能兼容也可能不兼容 - 取决于架构(可能在一种架构上兼容但在另一种架构上不同)。

考虑这个结构:

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 对齐)或 64 位/8B 对齐中省略。

x86 标准布局(包(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) = 4whileoffsetof(B,y) = 2和 union 不会改变这一点(因此&u.a.y != u.b.y

于 2014-07-26T11:03:12.323 回答
2

它看起来像标准中的一个洞。负责的事情是提交缺陷报告

不过有几件事:

  • 您的第一个示例并没有真正证明问题。short在 the 之后添加 achar也将具有将 对齐char到 2 字节边界的效果,而不会更改公共子序列。
  • alignas不是仅限 C++ 的;它同时添加到C11中。由于标准布局属性是一种跨语言兼容性工具,因此要求相应的对齐说明符匹配可能比使用非静态成员对齐说明符取消类的资格更为可取。
  • 如果成员对齐说明符属于成员的类型,则不会有问题。其他问题可能是由于缺乏对类型的调整而导致的,例如,ret fn( alignas(4) char )可能需要修改函数参数以使 ABI 正确处理它,但语言可能不提供这种调整。
于 2014-07-27T12:10:00.890 回答
0

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

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

即,我们可以编写以下不调用 UB 的代码吗?

因此,这不是UB,相关段落已被您引用。

编辑:对不起,这当然会导致 UB,因为成员之间的填充没有(或实现)定义(第 9.2/13 节)!我不小心误读了这个例子,因为我认为它访问的是 x 而不是 y,因为使用 x 它实际上总是有效的——而使用 y 它理论上不必这样做(尽管它实际上总是会这样做)。

于 2014-05-02T15:44:54.733 回答