我以前很舒服地使用过工会。今天,当我阅读这篇文章并得知此代码时,我感到震惊
union ARGB
{
uint32_t colour;
struct componentsTag
{
uint8_t b;
uint8_t g;
uint8_t r;
uint8_t a;
} components;
} pixel;
pixel.colour = 0xff040201; // ARGB::colour is the active member from now on
// somewhere down the line, without any edit to pixel
if(pixel.components.a) // accessing the non-active member ARGB::components
实际上是未定义的行为,即从工会的成员(而不是最近写给该成员的成员)中读取导致未定义的行为。如果这不是联合的预期用途,那是什么?有人可以详细解释一下吗?
更新:
我想在事后澄清一些事情。
- 对于C和C ++,问题的答案是不同的。我那愚昧无知的年轻人将其标记为C和C ++。
- 在通过C ++ 11的标准进行搜索之后,我不能得出结论说它要求访问/检查非活动工会成员是未定义/未指定/实现定义的。我只能找到§9.5/ 1:
如果一个标准布局联合包含多个共享公共初始序列的标准布局结构,并且此标准布局联合类型的对象包含一个标准布局结构,则可以检查任何一个的标准初始结构标准布局的struct成员。§9.2/ 19:如果相应成员具有与布局兼容的类型,并且两个成员都不是一个位字段,或者两个成员都是宽度相同的一个或多个初始序列的位字段,则两个标准布局结构共享一个公共的初始序列成员。
- 在C语言中(从C99 TC3-DR 283开始),这样做是合法的(感谢Pascal Cuoq提出了这一点)。但是,如果读取的值恰好对于读取的类型无效(所谓的“陷阱表示”),则尝试执行此操作仍可能导致未定义的行为。否则,读取的值是实现定义的。
C89 / 90在未指定的行为下对此进行了声明(附件J),K&R的书说它是定义的实现。来自K&R的报价:
这是联合的目的-单个变量可以合法地容纳多种类型之一。[...]只要用法是一致的:检索的类型必须是最近存储的类型。程序员有责任跟踪工会中当前存储的是哪种类型。如果将某种内容存储为一种类型并提取为另一种类型,则结果取决于实现。
摘自Stroustrup的TC ++ PL(重点是我的)
联合使用对于有时被误用于“类型转换 ” 的数据兼容性至关重要。
最重要的是,提出这个问题(自我提出要求以来,其标题保持不变)的目的是理解联合的目的,而不是标准允许的条件。例如,C ++标准当然允许使用继承进行代码重用,但是将继承引入C ++语言特性并不是目的或初衷。这就是Andrey的答案继续保持被接受的原因。
scouring C++11's standard I couldn't conclusively say that it calls out accessing/inspecting a non-active union member is undefined [...] All I could find was §9.5/1
...真?您在段落开始处引用了一个异常注释,而不是要点:“在联合中,最多一个非静态数据成员可以在任何时间处于活动状态,即,最多一个非静态数据成员可以处于活动状态。非静态数据成员可以随时存储在联合中。” -直到p4:“通常,必须使用显式的析构函数调用和放置新的运算符来更改联合的活动成员 ”
b, g, r,
并且a
可能不是连续的,因此与的布局不匹配uint32_t
。这是其他人指出的Endianess问题的补充。