管理联合的未初始化字节的规则是什么? (假设有些是初始化的)
下面是一个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没有给它们相同的地址。
我也尝试用另一个结构替换数组,但这并不会改变任何东西。
我无法从这里访问在线编译器,他们被我的工作阻止了。
答案 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)。