如何在C++中将结构体初始化为0

28

这里有一个相关的 C 语言答案,但在 C++ 中不起作用(作为结构体的零初始化器):Initializing a struct to 0。其中提出的解决方案之一是:

myStruct _m1 = {0}; 

这在C中可以正常工作,但在C++中不行 :( :

error: 无法使用类型为'int'的右值初始化类型为'myScope::MyStruct'的成员子对象。

如何在C++中对结构体进行零初始化?

相关:

  1. 在C中将结构体初始化为0:将结构体初始化为0
  2. 更新:(相邻但不是重复问题,也非常有用)使用空花括号进行初始化

另请参阅:

  1. [相邻关联的问题,但适用于C样式数组,而非结构体]如何将数组的所有成员初始化为相同的值?

对于投票关闭此问题的人们:

我的问题不是另一个问题的重复项(使用空花括号进行初始化),因为该问题并未询问有关在C ++中初始化结构的各种方法以及为什么C方式不起作用的问题,而是在问为什么C++关键字explicit破坏了他们的一种初始化技术。这是两个不同的问题。不是重复的。

我提出的后续问题:

  1. 为什么将C ++结构体初始化为= {0}不会将其所有成员都设置为0?

这个回答解决了你的问题吗?使用空花括号进行初始化 - rsjaffe
1
不,它并不相关,但仍然有用。我认为它们是两个不同的问题。 - Gabriel Staples
4个回答

40

开始前:

  1. Let me point out that a lot of the confusion around this syntax comes because in C and C++ you can use the = {0} syntax to initialize all members of a C-style array to zero! See here: https://en.cppreference.com/w/c/language/array_initialization. This works:

    // z has type int[3] and holds all zeroes, as: `{0, 0, 0}`
    int z[3] = {0}; 
    

    But, that syntax does not work the same for structs, which are entirely different animals than C-style arrays.

  2. See also my follow-up question I asked after writing this answer below: Why doesn't initializing a C++ struct to = {0} set all of its members to 0?


回到答案:

我想通了:要使其编译,只需删除零:

# does NOT work
myStruct _m1 = {0}; 

# works!
myStruct _m1 = {};

现在它可以编译了。但是,我运行了一堆测试来检查我在struct_initialization.cpp文件中的一些内容,在我的eRCaGuy_hello_world存储库中,它并没有将结构体的所有元素初始化为!相反,它将结构体初始化为其默认值。要运行我的测试并自己查看,请克隆我的上面的存储库并运行eRCaGuy_hello_world/cpp/run_struct_initialization.sh
假设你有这个结构体:
typedef struct
{
    int num1 = 100;
    int num2 = -100;
    int num3;
    int num4 = 150;
} data_t;

注意:上面的typedef是在我测试这些东西时使用C而不是C++的遗留问题(当然,在C中不允许使用默认结构值)。对于C ++,最好使用以下方法:


{{注意:上面的typedef是我在测试这些东西时在C而不是C++中使用的(尽管在C中不允许使用默认结构值)。对于C ++,最好使用以下方法:}}
struct data_t
{
    int num1 = 100;
    int num2 = -100;
    int num3;
    int num4 = 150;
};

所以请忽略我在下面不必要地使用typedef定义结构体。

无论如何,如果我声明上述任何一个data_t结构体,然后执行以下操作:

data_t d2 = {};
printf("d2.num1 = %i\nd2.num2 = %i\nd2.num3 = %i\nd2.num4 = %i\n\n",
       d2.num1, d2.num2, d2.num3, d2.num4);

输出将会是:

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

我甚至不确定d2.num3是否为零,是因为它被初始化为零还是因为它未被初始化而那个内存位置碰巧包含零。

如此解释:https://en.cppreference.com/w/cpp/language/zero_initialization,你也可以这样做:

myStruct _m1{};

在上面的示例中,这段代码:
data_t d2{};
printf("d2.num1 = %i\nd2.num2 = %i\nd2.num3 = %i\nd2.num4 = %i\n\n",
       d2.num1, d2.num2, d2.num3, d2.num4);

...将产生与我上面展示的输出完全相同的结果。

即使在设置结构体为= {0}有效的情况下,例如:

// Does NOT do what I expected! Only sets the FIRST value in the struct to zero! 
// The rest seem to use default values.
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

然而,在C语言风格的数组中(而不是结构体),这些语义是有效的。请参考此处的答案(如何将数组的所有成员初始化为相同的值?)。因此,以下这两行代码在使用C ++时都可以将C风格数组的所有元素设置为零:
uint8_t buffer[100] = {0}; // sets all elements to 0 in C OR C++
uint8_t buffer[100] = {};  // sets all elements to 0 in C++ only (won't compile in C)

因此,经过大量实验后,以下几种方式似乎是唯一的用于在C++中对结构体进行零初始化的方法。如果您知道其他方法,请在此处进行评论或留下您自己的答案。

C++中唯一可能的对结构体进行零初始化的方法有:

  1. Be explicit:

     // C-style typedef'ed struct
     typedef struct
     {
         int num1 = 100;
         int num2 = -100;
         int num3;
         int num4 = 150;
     } data_t;
    
     // EXPLICITLY set every value to what you want!
     data_t d1 = {0, 0, 0, 0};
     // OR (using gcc or C++20 only)
     data_t d2 = {.num1 = 0, .num2 = 0, .num3 = 0, .num4 = 0};
    
  2. Use memset() to force all bytes to zero:

     data_t d3;
     memset(&d3, 0, sizeof(d3));
    
  3. Set all default values to zero in the first place:

     // C-style typedef'ed struct
     typedef struct
     {
         int num1 = 0;
         int num2 = 0;
         int num3 = 0;
         int num4 = 0;
     } data_t;
    
     // Set all values to their defaults, which are zero in
     // this case
     data_t d4 = {};
     // OR
     data_t d5{}; // same thing as above in C++
    
     // Set the FIRST value only to zero, and all the rest
     // to their defaults, which are also zero in this case
     data_t d6 = {0};
    
  4. Write a constructor for the C++ struct

     // 1. Using an initializer list
     struct data
     {
         int num1;
         int num2;
         int num3;
         int num4;
    
         data() : 
             num1(0),
             num2(0),
             num3(0),
             num4(0) {}
     };
    
     data d7; // all values are zero
    
     // OR: 2. manually setting the values inside the constructor
     struct data
     {
         int num1;
         int num2;
         int num3;
         int num4;
    
         data()
         {
             num1 = 0;
             num2 = 0;
             num3 = 0;
             num4 = 0;
         }
     };
    
     data d8; // all values are zero
    
  5. Use a struct with no default values, and make your object you create from it static

     typedef struct
     {
         int num1;
         int num2;
         int num3;
         int num4;
     } data_t;
    
     // `static` forces a default initialization of zero for each
     // value when no other default values are set
     static data_t d9;
    
  6. So, if you have a struct with non-zero default values, and you want to zero all values, you must do it EXPLICITLY! Here are some more ways:

     // 1. Have a `constexpr` copy of the struct that you use to
     // reset other struct objects. Ex:
    
     struct data
     {
         int num1 = 1;
         int num2 = 7;
         int num3 = -10;
         int num4 = 55;
     };
    
     constexpr data DATA_ALL_ZEROS = {0, 0, 0, 0};
    
     // Now initialize d13 to all zeros using the above `constexpr` struct 
     // object
     data d13 = DATA_ALL_ZEROS; 
    
    
     // OR 2. Use a `zero()` member function to zero the values:
    
     struct data
     {
         int num1 = 1;
         int num2 = 7;
         int num3 = -10;
         int num4 = 55;
    
         zero()
         {
             num1 = 0;
             num2 = 0;
             num3 = 0;
             num4 = 0;
         }
     };
    
     data d14;
     d14.zero();
    
重要的结论是:这三种写法:data_t d{}data_t d = {}data_t d = {0}都不能将结构体的所有成员都设置为零!
  1. data_t d{}会将所有值设置为结构体中定义的默认值。
  2. data_t d = {}也会将所有值设置为默认值。
  3. data_t d = {0}只会将第一个值设置为零,其他值仍然是默认值。
因此,请明确指定需要的初始化值!
请注意,我写的以上主要结论似乎与cppreference.com上的文档相矛盾,这促使我提出了这个后续问题,对我的理解非常有帮助!

更深入的了解

  1. 最有用的我的后续问题:为什么将C++结构体初始化为= {0}不能将其所有成员都设置为0?

参考资料:

  1. 非常有用:
    1. https://en.cppreference.com/w/cpp/language/zero_initialization(零初始化)
    2. https://en.cppreference.com/w/cpp/language/aggregate_initialization(聚合初始化)
    3. https://en.cppreference.com/w/cpp/language/value_initialization(值初始化)
  2. 非常有用:将数组(而不是结构体)的所有成员初始化为相同的值:
    1. 如何将数组的所有成员初始化为相同的值?
    2. [仅适用于gcc] 如何将数组的所有成员初始化为相同的值?
  3. https://github.com/ElectricRCAircraftGuy/eRCaGuy_hello_world/blob/master/cpp/struct_initialization.cpp
    1. 克隆此存储库并使用cpp/run_struct_initialization.sh运行代码

相关:

  1. 在结构体中初始化默认值
  2. *****[我自己的答案,演示了在任何函数中修改结构体/重新分配成员的聚合方式:leds [0] = {10, 20, 30, 40, 50};] Arduino堆栈交换:初始化结构体数组

1
请注意,如果结构体类型不是“聚合体”,例如它定义了任何构造函数,则此语法不会进行零初始化。 - aschepler
如果类有用户提供的构造函数(在这种情况下不可能进行零初始化),则此语法不是零初始化。 - M.M
如果 myStruct _m1 = {0}; 无法编译通过,那么很有可能 {} 版本也无法对所有子对象进行零初始化。 - M.M
@M.M,好的,我进行了大量测试并完全重写了答案。请检查一下,看看是否有什么问题。data_t d = {0}的行为真的让我感到困惑,因为它甚至没有将结构体中的所有值都设置为零!相反,它只将结构体中的第一个值设置为零,这对我来说非常奇怪,然后它将所有其他值设置为它们的默认值。请参见我在参考文献底部的存储库以运行和测试我的大量测试代码。 - Gabriel Staples
1
“BE EXPLICIT”似乎是过于具体的建议。该语言旨在让类型设计人员在初始化时自行决定。因此,您应该重新考虑是否应该首先将其初始化为零,或者是否应该信任默认值。如果您拥有该类型,则应重新考虑这些默认值是否实际上应该为零。在完成这些工作后,如果您对特定初始化的考虑与类型的默认值不同,并且仍然必须初始化为零,则应明确指出。 - John Doggett
显示剩余6条评论

2

在C++中,对于没有构造函数(即可以被视为默认构造的)的类型,{}会将所有字节都初始化为零,与C中的{ 0 }完全相同。

PlainOldData pod{};

对于具有构造函数的类型,因此不能被轻松构造,将所有字节初始化为零几乎没有意义,因为这些对象的作用是控制自己的状态。通常您想要的是默认初始化,这通常可以使用{}完成。

Object obj{};
// or
Object obj;

你可以绕过这个限制,使用memset来清除内存,但要注意它会导致许多问题,如覆盖vtable指针等。
memset(&obj, 0, sizeof obj); // dangerous for non-trivial objects

"id est" 不是英语。我认为你可能混入了一些你的母语? - Gabriel Staples
5
我相信它起源于拉丁语,通常缩写为“i.e.”。 - Ayxan Haqverdili
1
啊...我一直使用“ie”或“i.e.”,但从未知道它是“id est”。 :) - Gabriel Staples

2
在C++中,对象具有控制其中任何子对象值的能力。因此,C++没有机制可以强制将零初始化应用于任何对象通常情况下
没有用户提供的构造函数或默认成员初始化程序的对象可以在两种情况下进行零初始化:如果变量声明为static,或者如果正在对对象进行值初始化。有多个语法会引起对象的值初始化,包括T()T{}T t = {};(适用时)。
但是,除此之外,如果对象类型的创建者不希望它被零初始化,则无法强制施加该操作。您可以要求值初始化或默认初始化,但是这两个操作是否会调用零初始化取决于类型。

1
小心使用 () - Ayxan Haqverdili

1
一般而言是不可能的。有些类可能无法提供访问所有成员的方法,在这种情况下,你最好做的就是初始化值,这将为你提供该类的默认版本。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接