正在阅读不是最近用GCC未定义行为编写的成员吗?

spa*_*ger 6 c++ struct unions

C++参考具有以下解释的工会,与粗体这个问题最有趣的部分:

工会只有拥有其最大数据成员所需的大小.其他数据成员以与该最大成员的一部分相同的字节分配.该分配的细节是实现定义的,并且从未最近编写的联合成员读取它是未定义的行为.许多编译器作为非标准语言扩展实现了读取联合的非活动成员的能力.

现在,如果我使用g++ -std=c++11以下代码在Linux Mint 18上编译,我会得到以下输出(由printf语句旁边的注释给出):

#include <cstdio>
using namespace std;

union myUnion {
    int var1; // 32 bits
    long int var2; // 64 bits
    char var3; // 8 bits
}; // union size is 64 bits (size of largest member)

int main()
{
    myUnion a;
    a.var1 = 10;
    printf("a is %ld bits and has value %d\n",sizeof(a)*8,a.var1); // ...has value 10
    a.var2 = 123456789;
    printf("a is %ld bits and has value %ld\n",sizeof(a)*8,a.var2); // ...has value 123456789
    a.var3 = 'y';
    printf("a is %ld bits and has value %c\n",sizeof(a)*8,a.var3); // ...has value y
    printf("a is %ld bits and has value %ld\n",sizeof(a)*8,a.var2); //... has value 123456789, why???
    return 0;
}
Run Code Online (Sandbox Code Playgroud)

在之前的行中return 0,a.var2read不是字符的ASCII小数'y'(这是我所期望的,我是工会的新手),而是它首先定义的值.基于cppreference.com的上述引用,我是否理解这是未定义的行为,因为它不是标准的,而是GCC的特定实现?

编辑

正如下面的大答案所指出的那样,我在printf之前的声明之后的评论中犯了一个复制错误return 0.正确的版本是:

 printf("a is %ld bits and has value %ld\n",sizeof(a)*8,a.var2); //... has value 123456889, why???
Run Code Online (Sandbox Code Playgroud)

即7变为8,因为前8位被'y'字符的ASCII值覆盖,即121(0111 1001以二进制形式).不过,我将保留上述代码中的内容,以保持与由此产生的重要讨论保持一致.

Xir*_*ema 4

未定义行为的有趣之处在于,它与“随机”行为非常不同。编译器在处理未定义的行为时将决定使用一种行为,并且每次都会表现出相同的行为。

举个例子:IDEOne对此代码有自己的解释:http://ideone.com/HO5id6

a is 32 bits and has value 10
a is 32 bits and has value 123456789
a is 32 bits and has value y
a is 32 bits and has value 123456889
Run Code Online (Sandbox Code Playgroud)

您可能会注意到那里发生了一些有趣的事情(抛开 IDEOne 的编译器long int是 32 位而不是 64 位这一事实)。它仍然将第 4 行显示为与第 2 行类似的读数,但该值实际上略有变化。似乎发生的情况是, 的char'y'已在联合中设置,但它没有更改任何其他位。long long int当我将其切换为而不是时,我得到了类似的行为long int

在您的示例中,您可能需要检查第 4 行是否与之前完全相同我有点怀疑事实是否如此。

无论如何,为了回答您的具体问题,TL;DR 是在 GCC 中,写入联合只会更改与您要写入的特定成员关联的位,并且不能保证更改/清除所有其他位。当然,就像任何与 UB 相关的事情一样,不要假设任何其他编译器(甚至同一编译器的更高版本!)都会表现相同。

  • _“编译器在处理未定义的行为时会决定使用一种行为,并且每次都会表现出相同的行为。”_不一定。 (2认同)