这个问题是关于将未经宣传的自动变量分配给同一类型的另一个的定义或其他方式。
考虑
typedef struct
{
int s1;
int s2;
} Foo;
typedef union
{
int u1;
Foo u2;
} Bar;
int main()
{
{
int a;
int b = a; // (1)
}
{
Foo a;
Foo b = a; // (2)
}
{
Bar a;
a.u1 = 0;
Bar b = a; // (3)
}
}
参考main
中的评论:
(1)未定义,因为a
未初始化。我知道的很多。
但是(2)呢?结构成员s1
和s2
未初始化。
此外,(3)怎么样?内存u2.s2是未初始化的,所以读取它是未定义的行为否?
答案 0 :(得分:4)
行为在(1)和(2)中未定义。
根据C标准,具有未初始化的自动存储持续时间的对象的值为不确定(C 2011 [N1570] 6.7.9 10)。名义上,这意味着它有一些价值,但我们不知道在编写程序时它是什么。
但是,该标准还说“如果左值指定了一个自动存储持续时间的对象,该对象可以使用寄存器存储类声明(从未使用过其地址),并且该对象未初始化(未使用初始化程序声明并且在使用之前未对其进行任何分配),行为未定义“(6.3.2.1 2)。在您的示例代码中,a
的地址从未被采用,并且未被初始化,并且在表达式中使用它是左值。因此,行为未定义。
(本段,6.3.2.1 2,旨在容纳可以检测未初始化寄存器使用的处理器。但是,C标准中的规则适用于所有实现。)
C标准没有明确说明(3)。虽然为了6.3.2.1 2的目的,已经为union的成员分配了一个值,因此没有未初始化,b = a
中使用的对象是union,而不是其成员。显然,我们的直观概念是,如果为一个联合的成员分配了一个值,那么联合就有一个值。但是,我没有在C标准中看到这一点。
我们可以推断6.3.2.1 2并不打算将联合或结构视为未初始化,至少如果它的一部分已被赋值,因为:
结构可以包含未命名的成员,例如未命名的位字段。
Per C 6.7.9 9,即使在初始化(结构)之后,未命名的结构成员也具有不确定的值。
如果6.3.2.1 2适用于并非每个成员都分配了值的结构,那么如果b = a
是具有未命名成员的结构并且具有a
,那么a
将始终未定义自动存储持续时间。
这似乎是不合理的,而不是标准的意图。
然而,这里有一些摆动空间。标准可以指定结构只有在初始化或所有的命名成员都已赋值时才未初始化。在这种情况下,如果R
是一个只为一个成员分配了值的结构,那么(3)将是未定义的。我不认为这个摆动的房间存在于一个联盟;如果工会成员被赋予了价值,那么认为工会不是未初始化是合理的。
答案 1 :(得分:3)
通常,从未初始化的对象分配不是未定义的行为,它只会使结果不明确。
但您展示的代码确实有未定义的行为 - 原因与您假设的不同。引用N1570(最新的C11草案),§6.3.2.1p2:
[...]如果 左值指定了一个自动存储持续时间的对象 使用
register
存储类声明(从未使用过地址)和该对象 未初始化(未使用初始化程序声明,并且没有对其进行任何分配) 在使用之前执行),行为未定义。
解释一下:C标准准备处理未存储在可寻址位置中的值。当它们被保存在CPU的一个寄存器中时,通常就是这种情况。显式地为对象提供register
存储类只是编译器的提示,如果合理的话,它应该将该对象保存在寄存器中。反过来说,只要代码不需要解决它(通过获取指针),编译器就可以自由地在寄存器中保存自动存储持续时间的任何对象。
在您的代码中,您拥有自动存储持续时间的未初始化对象,它们的地址永远不会被占用,因此编译器可以自由地将它们放在寄存器中。这意味着在初始化对象之前没有值(甚至不是未指定的对象)。因此,使用此可能不存在的值初始化另一个对象(或用于其他目的)是未定义的行为。
如果你的代码在所有这些例子中都会指向相应的a
,那么分配的结果将是未指定的(当然),但行为将被定义。
值得补充的是,struct
和union
与您的问题的答案无关。对于具有自动存储持续时间的所有类型的对象,规则是相同的。也就是说,在您的第三个示例中,在您指定a
的一个成员后,union
不再是未初始化的。因此,对于您的第三个示例,行为是明确定义的。 union
的其他成员中的内容无关紧要,union
一次只能为其中一个成员保留一个值。