【问题标题】:Why doesn't initializing a C++ struct to `= {0}` set all of its members to 0?为什么不将 C++ 结构初始化为 `= {0}` 将其所有成员设置为 0?
【发布时间】:2020-08-15 07:40:41
【问题描述】:

在进行了大量的测试和编写this answer on how to initialize a struct to zero in C++ 之后(注意:对它的否决是我完全重写它之前),我不明白为什么= {0} 没有设置结构的所有成员归零!

如果你这样做:

struct data_t
{
    int num1 = 100;
    int num2 = -100;
    int num3;
    int num4 = 150;
};

data_t d3 = {0};
printf("d3.num1 = %i\nd3.num2 = %i\nd3.num3 = %i\nd3.num4 = %i\n\n",
       d3.num1, d3.num2, d3.num3, d3.num4);

...输出是:

d3.num1 = 0
d3.num2 = -100
d3.num3 = 0
d3.num4 = 150

...虽然我预计输出是这样的:

d3.num1 = 0
d3.num2 = 0
d3.num3 = 0
d3.num4 = 0

...这意味着只有 FIRST 成员设置为零,其余的都设置为默认值。

我一直认为以这三种方式中的任何一种初始化结构都会对其进行零初始化,但显然我错了!

  1. data_t d{}
  2. data_t d = {}
  3. data_t d = {0}

因此,我从this answer 得到的主要收获是:

这里最大的收获是:data_t d{}data_t d = {}data_t d = {0},实际上将结构的所有成员都设置为零!

  1. data_t d{} 将所有值设置为结构中定义的默认值。
  2. data_t d = {} 还将所有值设置为默认值。
  3. data_t d = {0} 仅将 FIRST 值设置为零,而将所有其他值设置为其默认值。

那么,为什么不将 C++ 结构初始化为 = {0} 将其所有成员设置为 0?

请注意,我上面的关键要点实际上与我多年来一直使用的这个看起来相当官方的文档 (https://en.cppreference.com/w/cpp/language/zero_initialization) 相矛盾,它说 T t = {} ;T {} ; 都是零初始化器,而事实上,根据我上面的测试和外卖,它们不是。

参考资料:

  1. How to initialize a struct to 0 in C++
  2. 更新:我也被指向了这个参考:What does {0} mean when initializing an object?

【问题讨论】:

  • 为什么要这样?您只给出一个值,而不是所有属性的值。
  • 这是一个问题还是一个带有答案的问题?你应该把答案分成下面的答案部分。
  • @tadman:问题的后半部分陈述了what显然发生了。它没有回答所提出的问题,为什么
  • 你的 typedef 是多余的,c++ 中的所有类和结构都是类型
  • 高级开发人员在 C 和 C++11 之前是正确的。自 C++11 起,规则略有改变以适应默认成员初始化器。

标签: c++ struct initialization zero-initialization


【解决方案1】:

那么,为什么不将 C++ 结构初始化为 = {0} 将其所有成员设置为 0?

因为您只提供一个值,而该类有多个成员。

当您拥有T t{};T t = {} 时,您正在执行的操作称为value initialization。在值初始化中,如果对象/成员没有默认构造函数或默认成员初始化器,则编译器会回退到零初始化对象/成员。所以与

data_t d{}

按顺序排列的成员的值将是 100, -100, 0 ,150 并且 0 for num3 发生是因为它没有默认值并且您没有在 {} 中提供值所以编译器回零初始化num3。这与data_t d = {} 相同。使用data_t d = {0},您提供了第一个元素,所以num10,但是就像前两个一样,所有其他成员如果有一个,则使用其默认值初始化,如果没有,则初始化为零,为成员值提供 0、-100、0、150。

这是在 C++11 发布并允许默认成员初始化程序时发生的更改。


如果您的 data_t 被定义为

typedef struct
{
    int num1;
    int num2;
    int num3;
    int num4;
} data_t;

那么data_t d{}data_t d = {}data_t d = {0} 都会给你一个零初始化类,因为没有默认的成员初始化器和你提供的唯一值braced-init-list{...} 的技术名称)为零,因此所有成员都为零。

【讨论】:

  • 也许这是经典的 Stack Overflow“战略性投票”:(。感谢您的回答。这是迄今为止最清楚的,所以我将其标记为解决方案。请参阅我的 cmets 在另一个答案下同样,因为他们总结了你刚才所说的内容。这非常有启发性,我不知道其中存在多层微妙之处。我认为结构初始化是一个非常被误解的话题,即使是资深程序员也是如此。我希望你能接受也请查看我的其他问答,因为您有一些很棒的见解:How to initialize a struct to 0 in C++
  • 如果你只是调用data_t d;值初始化会发生吗?作为默认构造函数的一部分,默认值仍然被设置,但我想任何没有默认值的成员都不会被初始化,对吗?他们只会拥有位于该地址的 RAM 中的任何数据。这与data_t d{};data_t d = {}; 相反,或者我相信data_t d = data_t();,其中没有默认值的成员将被设置为零。你能确认我的理解是正确的吗?
  • @GabrielStaples data_t d; 被称为default initialization
  • 谢谢。虽然这个文档很难阅读,但我为默认初始化挑选的要点是为了回答我自己的问题,1)使用每个结构成员的默认值,2)如果没有默认值可用,“具有自动存储持续时间的对象(及其子对象)被初始化为不确定的值”,这意味着没有默认值的结构成员值未初始化并且可以是任何值,除了 3)“静态和线程本地”的情况objects [在这种情况下为结构]”,“初始化为零。”
【解决方案2】:

data_t d3 = {0}list-initialization 语法,它与data_t 等聚合一起执行aggregate initialization:提供的0 值用于初始化第一个成员,其余成员使用其相应的默认值进行初始化,如果不存在,则值初始化强调我的,为 C++14 编辑):

如果初始化子句的数量小于成员数或初始化列表完全为空,剩余成员 由它们的default member initializers 初始化,如果在类定义中提供,否则由空列表初始化,符合通常的list-initialization 规则(对非类类型和非聚合类执行value-initialization使用默认构造函数和聚合的聚合初始化)。如果引用类型的成员是这些剩余成员之一,则程序是非良构的。

value-initialization 表示非类类型的零初始化。 就是为什么没有默认值的成员num3得到0的值。

注意:不要与default-initialization 混淆,后者根本不初始化非类类型。 data_t d3; 将是 默认初始化,而成员 num3 将处于不确定状态。

需要注意的重要一点是被初始化的对象是否是一个聚合,因为聚合与具有构造函数的类的初始化规则是不同的。在构造函数的情况下,没有默认值的非类成员将被默认初始化(即处于不确定状态)。

一些例子:

struct A { // an aggregate
    int num1 = 100;
    int num2 = -100;
    int num3;
};

struct B { // not an aggregate
    int num1 = 100;
    int num2 = -100;
    int num3;
    B() {}
    B(int) {}
};

int main() {
    A a1; // default-initialization: a1 is {100, -100, ???}
    A a2 = {}; // aggregate initialization: a2 is {100, -100, 0}
    A a3 = { 1 }; // aggregate initialization: a3 is {1, -100, 0}
    A a4 = { 1,2,3 }; // aggregate initialization: a4 is {1, 2, 3}
    B b1; // default-initialization: b1 is {100, -100, ???}
    B b2 = {}; // copy-list-initialization invoking B::B(): b2 is {100, -100, ???}
    B b3 = { 1 }; // copy-list-initialization invoking B::B(int): b3 is {100, -100, ???}
    B b4 = { 1,2,3 }; // error: no B constructor taking (int,int,int)
}

另请注意,聚合初始化规则早于 C++11。例如,请参阅这个相关的 pre-C++11 问题:What does {0} mean when initializing an object?

【讨论】:

  • 最后一个链接 (What does {0} mean when initializing an object?) 很有帮助。所以,我发现了我理解中缺失的环节。我将加粗对我来说新的部分:使用= {0} 初始化结构零初始化结构的第一个成员,默认初始化结构的所有其他成员。表示结构的其他成员初始化为它们的默认值,而不是零,除非它们没有明确的默认值,在这种情况下它们被初始化为零。
  • 所以,在过去的几年里,当我认为 = {0} 是零初始化时,对于结构没有默认值的情况,我没有错,因为它对所有成员进行零初始化,因为没有成员具有默认值。但是,如果结构成员具有默认值,则它具有优先权,并被设置,但第一个成员除外,它显式设置为零。好的,我知道了。那里有很多我以前不理解的微妙之处。
  • @GabrielStaples:不,这些都不是微妙之处。您只是了解了列表初始化是如何错误地工作的。 braced-init-list 中的值列表用于初始化对象;这就是它的工作原理。如果列表只包含一个零,那么这意味着(通过列表初始化规则)将一个零应用于对象。就是这么简单。
  • @NicolBolas,很公平。至少我现在终于明白了。 “如果列表只包含一个零,那么这意味着(通过列表初始化规则)将一个零应用于对象”......我要添加:和所有其他成员都是value-initialized,这意味着如果有,则将它们设置为默认值,如果没有,则将其设置为零。
  • @GabrielStaples 当心“default-initializes all other members of the struct”实际上在 C++ 中意味着其他东西——对于非类类型,它意味着根本没有初始化。我已经扩展了我的答案以涵盖这一点,以防将来有人偶然发现这个:)
【解决方案3】:

简答:因为你有类内初始化器。

更长的答案:因为您正在为 C++14 或更高版本进行编译,所以具有类内初始化程序并且您正在使用 aggregate initialization。参考资料提供了解释:

如果初始化子句的数量小于 成员或初始化列表完全 为空,剩余成员被初始化 由它们的默认成员初始化器(如果在类中提供) 定义,否则 (C++14 起) 为空列表,根据 使用通常的列表初始化规则(执行 非类类型和非聚合类的值初始化 使用默认构造函数和聚合初始化 聚合)。

要对所有数据成员进行零初始化,只提供数据成员声明,​​不提供类内初始化器,然后使用= {0}={} 语法:

struct data_t
{
    int num1;
    int num2;
    int num3;
    int num4;
};

int main()
{
    data_t d3 = { 0 };
    printf("d3.num1 = %i\nd3.num2 = %i\nd3.num3 = %i\nd3.num4 = %i\n\n",
        d3.num1, d3.num2, d3.num3, d3.num4);
}

现在,您的所有数据成员都初始化为0

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2012-06-24
    • 2010-11-07
    • 2013-08-07
    • 1970-01-01
    • 1970-01-01
    • 2011-12-23
    • 2020-05-28
    相关资源
    最近更新 更多