联合或结构是否允许从未初始化的实例进行分配?

这个问题是关于将未经宣传的自动变量分配给同一类型的另一个的定义或其他方面。

考虑

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)呢? 结构成员s1s2未初始化。

那么,(3)呢? 内存u2.s2是未初始化的,所以读取它是未定义的行为吗?

行为在(1)和(2)中未定义。

根据C标准,具有未初始化的自动存储持续时间的对象的值是不确定的 (C 2011 [N1570] 6.7.9 10)。 名义上,这意味着它有一些价值,但我们不知道在编写程序时它是什么。

但是,该标准还说“如果左值指定了一个自动存储持续时间的对象,该对象可能已经使用寄存器存储类声明(从未使用过其地址),并且该对象未初始化(未使用初始化程序声明且未分配给它已在使用前执行),行为未定义“(6.3.2.1 2)。 在您的示例代码中,a的地址从未被采用,并且未被初始化,并且在表达式中使用它是左值。 因此,行为未定义。

(本段6.3.2.1 2旨在容纳可检测未初始化寄存器使用的处理器。但是,C标准中的规则适用于所有实现。)

(3)C标准没有明确说明。 尽管为了6.3.2.1 2的目的,已经为联合的成员分配了一个值,因此没有未初始化,但是在b = a使用的对象是联合,而不是其成员。 显然,我们的直观概念是,如果为一个union的成员分配了一个值,那么union就有一个值。 但是,我没有在C标准中看到这一点。

我们可以推断6.3.2.1 2并不打算将联合或结构视为未初始化,至少如果它的一部分已被赋值,因为:

  1. 结构可以包含未命名的成员,例如未命名的位字段。

  2. 根据C 6.7.9 9,即使在初始化(结构)之后,未命名的结构成员也具有不确定的值。

  3. 如果6.3.2.1 2适用于并非每个成员都分配了值的结构,那么如果a是具有未命名成员且具有自动存储持续时间的结构,则b = a将始终未定义。

  4. 这似乎是不合理的,而不是标准的意图。

但是,这里有一些摆动空间。 该标准可以指定只有在初始化结构或其所有命名成员都已分配值时,结构才不是未初始化的。 在这种情况下,如果a是仅为一个成员分配了值的结构,则(3)将是未定义的。 我不认为这个摆动的房间存在于一个联盟; 如果工会成员被赋予了价值,那么认为工会不是未初始化是合理的。

通常,从未初始化的对象分配不是未定义的行为 ,它只会使结果未指定。

但是你展示的代码确实存在未定义的行为 – 原因与你假设的不同。 引用N1570(最新的C11草案), §6.3.2.1p2

[…]如果左值指定了一个自动存储持续时间的对象,该对象可以使用register存储类声明(从未使用过其地址),并且该对象未初始化(未使用初始化程序声明并且没有赋值给它在使用之前执行过),行为未定义。

稍微解释一下:C标准准备处理未存储在可寻址位置的值 。 当它们被保存在CPU的一个寄存器中时,通常就是这种情况。 明确给出一个对象register存储类只是对编译器的一个提示 ,如果合理的话,它应该将该对象保存在寄存器中。 反过来说,只要代码不需要解决它(通过获取指针),编译器就可以自由地在寄存器中保存具有自动存储持续时间的任何对象。

在您的代码中,您有未初始化的对象,其自动存储持续时间从未获取其地址,因此编译器可以自由地将它们放在寄存器中。 这意味着在初始化之前,对象(甚至是未指定的对象)没有任何值 。 因此,使用此可能不存在的值来初始化另一个对象(或者,出于其他目的)是未定义的行为

如果你的代码在所有这些例子中都指向了相应的a ,那么分配的结果将是未指定的(当然),但是行为将被定义。


值得补充的是, structunion与你的问题的答案无关。 对于具有自动存储持续时间的所有类型的对象,规则是相同的。 也就是说,在你的第三个例子中,在你指定一个union成员后, a不再是未初始化的。 因此,对于您的第三个示例,行为是明确定义的。 无论union中的其他成员是什么, union一次只能为其中一名成员持有一个价值。