有人可以解释这两个启动器之间的区别吗?

Can someone please explain the difference between these two initiliazers?

我想知道是否有人可以对以下两段代码之间的区别提供详细、简单的解释。给定以下定义:

typedef struct {
    stuff;
    stuff_2;
} variable_t;

有什么区别:

如果我执行第一个,然后从未完全初始化它,为什么编译器不抛出错误?

注意:我正在使用 gcc -std=gnu99 进行编译,所以第二个是有效的并且最终成为我遇到的问题的解决方案。我想知道为什么。

当您申报时:

variable_t my_variable;           // a variable_t that is uninitialized

variable_t my_variable = {};      // a variable_t initialized with zeroes.

请注意,对于在文件范围内声明的变量,这并不重要,因为数据通常在程序启动前清零。

在堆栈上使用,第二行有效地用零填充 my_variables。就好像有一个电话:

memset(&variable, 0, sizeof(variable));

这是可行的,因为在 C 中,您可以使用 = 复制 struct

这是一个计算机肯定会赢的小游戏。

struct A { /*...*/ };

void main() 
{
   A a;  // random
   A b = {};
   if (memcmp(&a, &b, sizeof(A)) == 0)
   {
       printf("You win\n");
       return;
   }

   a = b;
   if (memcmp(&a, &b, sizeof(A)) == 0)
   {
       printf("I win\n");
   }
}

这在一定程度上取决于您放置相应变量定义的位置,而且似乎还取决于所使用的编译器。

自动存储时长

让我们讨论一下当变量具有自动存储持续时间时的区别(如果将它放在函数或块作用域中并且没有 static 关键字就是这种情况):

void someFunction() {
   variable_t my_variable;       // (1)
   variable_t my_variable = {};  // (2)
}

(1) 表示没有显式初始化的变量定义。根据这个 online C standard draft,它的值是不确定的:

If an object that has automatic storage duration is not initialized explicitly, its value is indeterminate.

(2) 是一个变量定义,通过没有指示符的初始化列表进行显式初始化,即不通过名称将值与成员相关联,而仅通过值的顺序(参见 6.7.9 p17..21)。

有趣的段落是6.7.9 p21,其中指出如果初始化列表的条目少于结构成员的数量,则根据静态存储持续时间的初始化规则(即0NULL 稍后解释):

If there are fewer initializers in a brace-enclosed list than there are elements or members of an aggregate, ... , the remainder of the aggregate shall be initialized implicitly the same as objects that have static storage duration.

所以好像写variable_t my_variable = {},那么所有的成员都初始化为0或者NULL

然而,aschepler 在评论中提到,C initialization list grammar states that initializer lists must not be empty (cf. also cppreference.com):

... the initializer must be a non-empty, brace-enclosed, comma-separated list of initializers for the members

所以根据标准,C 中的初始化列表至少需要一个条目;在我的 XCode8.3 环境中使用 -std=gnu99 测试它时,似乎支持一个空的初始化列表,但我知道这不是有效的参考。所以为了安全而不依赖于特定的编译器扩展,你实际上应该写:

   variable_t my_variable = {0};  

静态存储时长

在文件范围内,您的变量定义将具有静态存储持续时间,然后应用其他规则(参见 6.7.9 (10)):

(10) ... If an object that has static or thread storage duration is not initialized explicitly, then:

  • if it has pointer type, it is initialized to a null pointer;
  • if it has arithmetic type, it is initialized to (positive or unsigned) zero;
  • if it is an aggregate, every member is initialized (recursively) according to these rules, and any padding is initialized to zero bits;
  • if it is a union, the first named member is initialized (recursively) according to these rules, and any padding is initialized to zero bits;

...

(21) If there are fewer initializers in a brace-enclosed list than there are elements or members of an aggregate, ... the remainder of the aggregate shall be initialized implicitly the same as objects that have static storage duration.

所以如果你写...

#include <stdio.h>
variable_t my_variable;       // (1)
variable_t my_variable = {};  // (2)

然后 (1) 和 (2) 实际上产生相同的结果,因为对于未显式初始化的变量 (1),段落 (10) 适用,而对于显式但空的初始化变量 (2),根据段落(21),每个成员回退到(10)的初始化规则。

同样,如上所述,编译器可能不支持空的初始化列表。

希望对您有所帮助(因为打了很多字:-))