我之前很舒服地使用了工会;今天当我看到这篇文章并知道这段代码时我很震惊
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:
如果标准布局联合包含多个共享公共初始序列的标准布局结构,并且此标准布局联合类型的对象包含标准布局结构之一,则允许检查任何标准布局的公共初始序列标准布局结构成员。§9.2/19:两个标准布局结构共享一个共同的初始序列,如果相应的成员具有布局兼容的类型,并且两个成员都不是位域或两者都是一个或多个初始序列的具有相同宽度的位域成员。
- 在 C 语言中(C99 TC3 - DR 283起)这样做是合法的(感谢 Pascal Cuoq提出这个问题)。但是,如果读取的值恰好对于它所读取的类型无效(所谓的“陷阱表示”),那么尝试这样做仍然会导致未定义的行为。否则,读取的值是实现定义的。
C89/90 在未指定的行为(附件 J)下指出了这一点,K&R 的书说它是定义的实现。来自 K&R 的报价:
这就是联合的目的——一个可以合法地持有几种类型中的任何一种的单一变量。[...] 只要用法一致:检索到的类型必须是最近存储的类型。程序员有责任跟踪当前存储在联合中的类型;如果将某些内容存储为一种类型并提取为另一种类型,则结果取决于实现。
摘自 Stroustrup 的 TC++PL(重点是我的)
联合的使用对于数据的兼容性可能是必不可少的 [...]有时误用于“类型转换”。
最重要的是,提出这个问题(自我问起,其标题保持不变)的目的是了解联合的目的,而不是标准允许的内容。例如,C++ 标准当然允许使用继承进行代码重用,但是这不是将继承作为 C++ 语言特性引入的目的或初衷。这就是安德烈的答案继续被接受的原因。