当访问联合中不同类型的不同成员进行写和读操作时,C和C++中的未定义行为有区别吗



让我们立即从示例代码开始:

#include <stdio.h>
#include <stdbool.h>
typedef union {
bool b;
int i;
} boolIntUnion;
int main(void) {
boolIntUnion val;
val.i = 0;
printf("i = %d; %sn", val.i, ((val.b) ? "true" : "false"));

val.i = 1;
printf("i = %d; %sn", val.i, ((val.b) ? "true" : "false"));

val.i = 2;
printf("i = %d; %sn", val.i, ((val.b) ? "true" : "false"));
}

我现在的问题是,你可以在这里看到,使用并集是否是某种未定义的行为。我感兴趣的是第三种情况,我将val.i设置为2,但随后通过val.b使用并集中的布尔值作为打印字符串的条件。在MSVC 19和gcc中,我得到了2在并集中的预期行为,从而从val.b得到true的值,但对于Clang(和Clang-cl(,我得到false。我怀疑是编译器错误,因为如果我查看Clang的汇编输出,我会看到test cl, 1,而MSVC和gcc分别给我test eax, eaxtest al, al。我在Godbolt上用包括执行输出在内的所有三个编译器测试了这一点。在中间拍手是你可以看到不同行为的地方。[1]

现在,我不确定在任何C或C++标准下,以这种方式使用并集是否是未定义的行为。这基本上是我现在的问题。如果这是有效的代码,我认为,我会用Clang/LLVM提交一个错误。只是想事先确定一下。我用Clang和Clang++测试了这一点,行为相同。

[1]https://godbolt.org/z/c95eWa9fq

在C++中,使用除最后存储的成员之外的联合成员是未定义的行为,具有常见初始布局的结构除外。

C++2020草案N4849[class.union](11.5(2表示"……并集类型的对象的非静态数据成员中最多有一个可以在任何时候处于活动状态……"并指出,检查并集中具有公共初始序列的结构中的成员是一个例外。[basic.life](6.7.3(1.5表示,当(除其他可能性外("对象占用的存储……被未嵌套在o(6.7.2(中的对象重用"时,对象o的生存期结束。因此,在val.i = …;之后,val.b的生存期已经结束(如果它开始的话(,并且访问val.b的行为不受C++标准的定义。C++标准允许程序或编译器的任何输出或其他行为。

根据C 2018注释99,在C中,访问除最后存储的成员之外的并集成员会导致在新类型中重新解释并集的适用字节(谈到6.5.2.3 3,其中规定访问具有.->的并集会提供命名成员的值,无论它是否是最后存储的会员(。

最新更新