c - union 或 struct 允许从未初始化的实例赋值吗?

标签 c language-lawyer

这个问题是关于将一​​个未初始化的自动变量分配给另一个相同类型的变量的定义性或其他方式。

考虑

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)。名义上,这意味着它有一些值(value),但我们在编写程序时并不知道它是什么。

但是,该标准还说“如果左值指定了一个自动存储持续时间的对象,该对象可以用register存储类声明(从未使用过它的地址),并且该对象未初始化(未使用初始值设定项声明,并且在使用之前未对其执行任何赋值),行为未定义”(6.3.2.1 2)。在您的示例代码中,从未获取 a 的地址,也未对其进行初始化,在表达式中使用它是一个左值。因此,行为是未定义的。

(本段 6.3.2.1 2 旨在适应可以检测未初始化寄存器的使用的处理器。尽管如此,C 标准中的规则适用于所有实现。)

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

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

  1. 结构可以有未命名的成员,例如未命名的位域。

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

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

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

但是,这里有一些回旋余地。该标准可以指定只有当一个结构被初始化或者其命名成员的所有 都被赋值时,该结构才不会未初始化。在这种情况下,如果 a 是一个结构,其中只有一个成员被赋值,那么 (3) 将是未定义的。我认为 union 不存在这种回旋余地;如果 union 体的成员已经被赋值,则认为 union 体没有未初始化是唯一合理的。

关于c - union 或 struct 允许从未初始化的实例赋值吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/47430860/

相关文章:

c++ - 为什么只有在声明自定义构造函数时才能访问基类析构函数?

c++ - 是什么构成了未指定的 block 作用域静态或线程存储持续时间变量的初始化失败?

c fgets 卡在文件开头

c - 创建 newNode 时没有从 createNode(int) 函数返回创建的节点

c++ - 实例化和特化的定义相互依赖

c - 使用过多的参数调用 printf 是未定义的行为吗?

c++ - 如果一个引用被声明为指向它自己呢?例如。整数& x = x;

c - 值(value)在C++中正在发生变化,我不知道为什么

c - 限制C程序的CPU使用

c - 使用 fgets 从文件中读取一行