联盟初始化

时间:2014-11-07 02:53:13

标签: c++ initialization unions

管理联合的未初始化字节的规则是什么? (假设有些是初始化的)

下面是一个32字节的联合,我只通过第一个成员初始化前16个字节。 似乎剩余的字节是零初始化的。这对我的用例很有用,但我想知道这背后的规则是什么 - 我在期待垃圾。

#include <cstdint>
#include <iostream>

using namespace std;

union Blah {
   struct {
      int64_t a;
      int64_t b;
   };
   int64_t c[4];
}

int main()
{
   Blah b = {{ 1, 2 }}; // initialize first member, so only the first 16 bytes.

   // prints 1, 2, 0, 0 -- not 1, 2, <garbage>, <garbage>
   cout << b.c[0] << ", " << b.c[1] << ", " << b.c[2] << ", " << b.c[3] << '\n';

   return 0;
}

我使用-O3 -Wall -Wextra -pedantic编译了GCC 4.7.2(最后一个需要为匿名结构命名)。希望能让我免于幸运。

我还尝试在堆栈上覆盖两个不同范围的两个变量,但是gcc没有给它们相同的地址。

我也尝试用另一个结构替换数组,但这并不会改变任何东西。

我无法从这里访问在线编译器,他们被我的工作阻止了。

3 个答案:

答案 0 :(得分:6)

C11标准6.2.6.1.7中最相关的部分,而不是专门针对初始化:

  

当一个值存储在union类型的对象的成员中时,   对象表示的字节与该对应的字节不对应   成员,但确实对应其他成员采取未指定的值。

第6.7.9.17节说:

  

每个大括号括起的初始化列表都有一个关联的当前对象。   当没有指定时,当前对象的子对象是   根据当前对象的类型按顺序初始化:   数组元素增加下标顺序,结构成员在   声明顺序,以及第一个指定的联合成员。

但没有明确说出并说其他位未初始化。对于静态联合,6.7.9.10说:

  

根据这些成员初始化(递归)第一个命名成员   规则,任何填充都初始化为零位;

所以第一个命名成员和任何填充位将被零初始化,但是对应于其他(通过暗示,更大)联合成员的位将是未指定的。

所以你不能指望那些额外的字节被初始化为零。

请注意,从技术上讲,即使您将c数组初始化为零,当您在struct中存储某些内容时,这些多余的位也会再次指定,并且您无法依靠他们仍然是零。那里有很多代码,假设这是真的(例如将一个char数组放在一个联合中以访问各个字节),实际上它可能是,但标准并没有这样做。保证它。

答案 1 :(得分:1)

union的括号内置初始化程序仅允许初始化第一个成员。这很好,初始化程序会初始化匿名结构,并使第一个成员成为活动成员。

在C ++中,联盟中只有一个成员可以随时处于活动状态。尝试通过联合读取其他成员会导致未定义的行为。尝试通过将它们作为字符类型别名来读取它们会给出未指定的值。

答案 2 :(得分:0)

所以我要说观察到的行为得到了标准的支持。

6.7.9(初始化)声明12中的ISO / IEC 9899:201x说:

  

如果用大括号括起来的列表中的初始化程序少于元素或成员   用来初始化已知数组的字符串文字中的总计或更少的字符   大于数组中元素的大小,余数应为   初始化与具有静态存储持续时间的对象一样隐式

静态对象初始化为0(请参见6.7.9.10或The initialization of static variables in C)。