2012-10-12 117 views
1

我刚开始我的追求与工会时,我发现一个很奇怪的C++联盟成员未初始化

如果我运行这个程序

#include <iostream> 
    using namespace std; 
    union myun{ 
    public: 
    int x; 
    char c; 
    }; 

    int main() 
    { 
    myun y; 
    //y.x=65; 
     y.c='B'; 
    cout<<y.x; 
    } 

输出是一些垃圾值,如果改变其价值并没有改变yc 接着我这样做

#include <iostream> 
    using namespace std; 
    union myun{ 
    public: 
    int x; 
    char c; 

    }; 
    int main() 
    { 
     myun y; 
     y.x=65; 
     y.c='B'; 
     cout<<y.x; 
    } 

输出是如预期为66,因为y.c = 'B' 通过其ASCII值(66)替换65。 任何人都可以解释第一个案例吗?

+1

在设置另一个之后从另一个读取的是UB,IIRC。 – chris

回答

2

这实际上是未定义的行为,从不是写入的最后一个联合成员读取。

你可以做到这一点,如果在联盟内的项目是布局兼容(如标准中所定义的),但这里并非如此与intchar(更准确地说,它可能是如果案件这两种类型的位宽相似,但通常情况并非如此)。

从C++ 03标准(由所取代C++ 11现在但仍相关):

在联合中,数据成员的至多一个可以在任何时间是活动的,即,最多一个数据成员的值可以随时存储在工会中。

我想你可能想看看reinterpret_cast如果你想做这种叠加活动。


在什么实际的覆盖下的第一个,数字输出的十六进制值发生条件:

-1218142398 (signed) -> 3076824898 (unsigned) -> B7649F42 (hex) 
                 == 
                 ^^ 
                 || 
             0x42 is 'B' ----++ 

应提供线索。 y.c='B'只设置该结构的单个字节,而其余三个字节(在我的情况下)不确定。

通过在该点之前放入y.x=65行,它将全部设置为四个字节,其中三个备用设置为零。因此,当您在以下分配中设置单个字节时,它们将保持为零。

+0

不仅因为关于联合中的活动成员的规则而导致这种未定义的行为,而且如果存储一个成员和读取另一个成员需要访问存储成员的值,那么这将是严格的别名违规。另外,我没有看到标准中提到不同类型的对象是相同宽度的布局兼容的东西,所以我不认为如果char和int的宽度相同兼容。 – bames53

+0

'static_cast'?你的意思是'reinterpret_cast'? –

+0

对不起,大卫,你是绝对正确的,修复它。bames53,我没有说这会使他们layout_compatible,我说它_could._它可能是因为int和char都是算术类型,但我不确定它是有保证的。 – paxdiablo

1

那么,你有点解释了第一个案例,当你显示你对第二个案件的理解。

初始化字符部分只修改数据类型中的一个字节,该数据类型提供了int。假设32位int,这意味着3个字节仍然未初始化...因此垃圾。

这里是你的工会的内存使用情况:

   byte 
      0 1 2 3 
     +------------ 
myun::x | X X X X 
myun::c | X - - - 

当您设置x,您可以设置一个整数,因此所有剩余的字节被初始化。当你设置c时,你只能修改一个字节。

1
y.c='B'; 
cout<<y.x; 

这有未定义的行为。在任何时候,工会只有一个成员。如果它实际上包含char成员,则无法尝试阅读int成员。因为这个行为没有定义,所以编译器可以用代码来完成它想要的功能。

1

因为sizeof(int) != sizeof(char)

也就是说,一个整数和一个字符占用不同数量的内存(在现今的普通计算机中,int是4个字节,char是1个字节)。工会只有它最大的成员。因此,当你设置字符时,你只设置1字节的内存 - 其他3字节只是随机垃圾。

即可以将联盟的最大成员第一次,或做类似:

memset(&y, 0, sizeof(y)); 

零填充整个联盟。

1

在一个联合中,分配的内存等于最大成员的大小,在你的情况下,它的大小是int,即在16位编译器的情况下是2个字节。所有成员都使用相同的内存空间来存储他们的数据,因此实际上,一次只能存储一种类型的成员。

当您将值'B'赋值给char成员时,它将在其1字节的内存空间中存储66。 然后你试着输出int成员的值,但是它试图通过读取2字节内存中的值来计算一个值,因此你得到了一个垃圾值。

0

POD类型的局部变量(更具体地说是堆栈中的变量,即具有存储类“自动”的)在声明时不会初始化为任何内容,因此3个字节(或64位系统上的7个字节)不受您的作业影响y.c将包含随机垃圾。

另外请注意,受分配y.c特定字节取决于CPU的字节序,所以这段代码将不同的表现在不同的系统,即使你分配给y.c之前初始化y.x

0

变量y是联合类型,y的长度是四个字节。举例来说,Y的内存布局是这样的:

--------------------------------- 
| byte1 | byte2 | byte3 | byte4 | 
--------------------------------- 

1)在第一个节目,这句话y.c='B';刚刚成立字节1,但字节2,字节3,字节4是在栈中的随机值。

2)在第二个程序中,句子y.x = 65;将byte1设置为65,byte2,byte3,byte4为零。然后,句子y.c='B';将byte1设置为'B'的整数ASCII值,因此给出66的输出。