用gcc写信给工会

Bar*_*rry 7 c++ gcc g++ unions

考虑以下类型:

struct A { int x; };
struct B { int y; char z; };
union U { A a; B b; };
Run Code Online (Sandbox Code Playgroud)

而这段代码片段:

U u;
new (&u.b) B;
b.y = 42;
b.z = 'x';
Run Code Online (Sandbox Code Playgroud)

在这一点上,读取u.a.x是明确定义的行为(并将产生42),因为它在共同的初始序列中.u.a.x是不确定的行为.

但是gcc允许通过联合进行类型惩罚 - 文档明确允许读取非活动成员,而不管成员顺序如何.gcc是否允许写入非活动成员,甚至是常见的初始序列,或者这仍然是gcc上未定义的行为?也就是说,如果在这一点上,我有:

void write(A& arg) { a.x = 17; }
write(&u.a); // generally undefined behavior, since active member is u.b
             // but does gcc allow it?
f(u.b.y);    // is this definitely f(17)?
g(u.b.z);    // ... and is this definitely g('x')?
Run Code Online (Sandbox Code Playgroud)

Lir*_*aro 1

您所描述的行为(称为 \xe2\x80\x9c类型双关\xe2\x80\x9d)是允许的,但 C++ 规范未定义(请参阅下面的注释)。它可能在特定的编译器和硬件下定义。\n更具体地说,在具有简单类型(char、short、float、double...)的 gcc 和 x86 上,这将充当不同字段之间的重新解释强制转换。

\n\n
\n

...从与最近写入的成员不同的联合成员(称为 \xe2\x80\x9ctype-punning\xe2\x80\x9d)读取的做法很常见。即使使用-fstrict-aliasing,也允许类型双关,只要通过联合类型访问内存即可。(来源

\n
\n\n

此外,有时它很有用,例如从设备(例如套接字)读取时:

\n\n
union {\n    struct {\n        int a;\n        char b;\n        short c;\n    } data;\n\n    char buf[128];\n} u;\n\nread_from_device(u.buf, 128);\nprintf("Data (a,b,c): (%d,%d,%d)\\n", u.data.a, u.data.b, u.data.c);\n
Run Code Online (Sandbox Code Playgroud)\n\n

首先,我们从设备读取原始数据,然后使用结构将其重新解释为数字。\n我们经常使用#pragma packon astruct来确保数据的打包方式与设备上的打包方式相同。

\n\n

活跃会员注意事项

\n\n

活动成员的确定是隐式的,并且仅由程序员而不是编译器确定。由您决定哪个成员处于活动状态。\n该字段的生命周期从您分配给该字段时开始。

\n\n
\n

...其生命周期的开始顺序是在左右操作数的值计算之后和赋值之前。

\n
\n\n

事实上,手册指定写入非活动字段使其成为活动字段,这表明这是允许的。

\n\n

请参阅了解更多信息:

\n\n
\n

会员生命周期

\n\n

联合成员的生命周期从成员激活时开始。\n 如果另一个成员之前处于活动状态,则其生命周期结束。

\n\n

当联合体的活动成员由 E1 = E2 形式的赋值表达式切换时,该表达式使用内置赋值运算符或普通赋值运算符,对于成员访问中出现的每个联合成员 X和 E1 的数组下标子表达式\n 不是具有非平凡或已删除默认构造函数的类,如果\n 修改 X 在类型别名\n 规则下将具有未定义的行为,则隐式创建 X 类型的对象\n 指定存储;不执行初始化,并且其生命周期的开始顺序是在左操作数和右操作数的值计算之后、赋值之前。

\n
\n\n
union A { int x; int y[4]; };\nstruct B { A a; };\nunion C { B b; int k; };\nint f() {\n  C c;               // does not start lifetime of any union member\n  c.b.a.y[3] = 4;    // OK: "c.b.a.y[3]", names union members c.b and c.b.a.y;\n                     // This creates objects to hold union members c.b and c.b.a.y\n  return c.b.a.y[3]; // OK: c.b.a.y refers to newly created object\n}\n\nstruct X { const int a; int b; };\nunion Y { X x; int k; };\nvoid g() {\n  Y y = { { 1, 2 } }; // OK, y.x is active union member (9.2)\n  int n = y.x.a;\n  y.k = 4;   // OK: ends lifetime of y.x, y.k is active member of union\n  y.x.b = n; // undefined behavior: y.x.b modified outside its lifetime,\n             // "y.x.b" names y.x, but X\'s default constructor is deleted,\n             // so union member y.x\'s lifetime does not implicitly start\n}\n
Run Code Online (Sandbox Code Playgroud)\n