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

Gab*_*les 32 c++ struct initialization

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

myStruct _m1 = {0}; 
Run Code Online (Sandbox Code Playgroud)

这在 C 中工作正常,但在 C++ 中不起作用。:( :

错误:无法使用“int”类型的右值初始化“myScope::MyStruct”类型的成员子对象。

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

有关的:

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

也可以看看:

  1. [相邻相关,但适用于 C 风格数组,而不是结构]如何将数组的所有成员初始化为相同的值?

致投票结束此问题的人们:

我的问题不是这个其他问题的重复(Initialization with empty curlybraces),因为这个其他问题不是询问在 C++ 中初始化结构体的各种方法以及为什么 C 方法不起作用,而是它们是询问为什么 C++ 关键字会explicit破坏他们的初始化技术之一。两个不同的问题。不重复。

我问的后续问题:

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

Gab*_*les 49

在我们开始之前:

  1. 让我指出,围绕此语法的许多混乱是因为在 C 和 C++ 中,您都可以使用该语法将C 样式数组= {0}的所有成员初始化为零!请参阅此处: https: //en.cppreference.com/w/c/language/array_initialization。所以,这有效:

    // z has type int[3] and holds all zeroes, as: `{0, 0, 0}`
    int z[3] = {0}; 
    
    Run Code Online (Sandbox Code Playgroud)

    但是,该语法对于struct 的作用并不相同,它们是与 C 风格数组完全不同的动物。此外,C 和 C++ 之间允许结构初始化的各种技术也有所不同。

    更进一步,人们应该知道,从技术上讲,= {0}仅将数组的第一个0元素显式初始化为,其余元素会隐式自动初始化为,0因为它们的值未指定,因此它们得到值初始化(对于非类类型为零初始化;请参阅此处)至0

    这些来自最新 cppreference.com 社区 wiki ( https://en.cppreference.com/w/c/language/array_initialization ) 的修订示例使这一点更加清晰。特别注意该int y[5] = {1,2,3};示例没有指定最后两个元素,因此它们隐式默认初始化为 0,并且该int z[4] = {1};示例仅显式将第一个元素初始化为 1,因此其余所有元素都隐式默认初始化为 0,并且该int w[3] = {0};示例仅显式地将第一个元素初始化为零,因此其余所有元素都隐式默认初始化为零:

    从大括号括起来的列表初始化

    当使用大括号括起来的初始化器列表初始化数组时,列表中的第一个初始化器将索引零处的数组元素初始化(除非指定了指示符)(自 C99 起),并且每个后续初始化器不带指示符(自 C99 起)初始化索引处的数组元素,该索引比前一个初始化器初始化的索引大一。

    // z has type int[3] and holds all zeroes, as: `{0, 0, 0}`
    int z[3] = {0}; 
    
    Run Code Online (Sandbox Code Playgroud)

    不过,上面的例子同样适用于数组,而这个答案适用于结构。那么,让我们继续吧。

  2. 另请参阅我在下面写下此答案后提出的后续问题:Why does notinitializing a C++ struct to = {0}set all of its member to 0?


回到答案:

我想通了:要让它编译,只需删除零:

int x[] = {1,2,3}; // x has type int[3] and holds 1,2,3
int y[5] = {1,2,3}; // y has type int[5] and holds 1,2,3,0,0
int z[4] = {1}; // z has type int[4] and holds 1,0,0,0
int w[3] = {0}; // w has type int[3] and holds all zeroes
Run Code Online (Sandbox Code Playgroud)

现在可以编译了。但是,我运行了一系列测试来检查eRCaGuy_hello_world存储库中的struct_initialization.cpp文件中的一些内容,并且使用不会将结构的所有元素初始化为零!相反,它将结构初始化为其默认值,称为“值初始化”(请参阅​​此处的答案)。要运行我的测试并亲自查看,请克隆上面的存储库并运行.= {}eRCaGuy_hello_world/cpp/struct_initialization_run.sh

假设你有这个结构:

// does NOT work in C++ (but *does* work in C)
myStruct _m1 = {0}; 

// works in both C and C++!
myStruct _m1 = {};
Run Code Online (Sandbox Code Playgroud)

注意:typedef上面的内容是我在 C 而不是 C++ 中测试这些东西时遗留下来的(当然,尽管 C 中不允许默认的结构值)。对于 C++,这是首选:

typedef struct
{
    int num1 = 100;
    int num2 = -100;
    int num3;
    int num4 = 150;
} data_t;
Run Code Online (Sandbox Code Playgroud)

因此,如果我不必要地使用它typedef来定义下面的结构,请忽略它。

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

struct data_t
{
    int num1 = 100;
    int num2 = -100;
    int num3;
    int num4 = 150;
};
Run Code Online (Sandbox Code Playgroud)

...输出将是:

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);
Run Code Online (Sandbox Code Playgroud)

我什至不确定是否d2.num3为零,因为它已初始化为零,或者因为它未初始化,而该内存位置恰好包含零。

所发生的情况是将= {}所有值设置为其默认值,即100member num1-100membernum2150member 的默认值num4num3我们没有设置显式的默认值,因此它被初始化为零作为其隐式默认值。阅读我的后续问题的这些答案:

  1. @NathanOliver 的回答在这里

    当你有T t{};T t = {}你正在做的事情称为值初始化。在值初始化中,如果对象/成员没有默认构造函数或默认成员初始值设定项,则编译器将回退到零以初始化对象/成员。所以与

    d2.num1 = 100
    d2.num2 = -100
    d2.num3 = 0
    d2.num4 = 150
    
    Run Code Online (Sandbox Code Playgroud)

    成员的值按顺序为 100, -100, 0 ,150 ,发生这种情况是0因为num3它没有默认值,并且您没有在 中提供值,{}因此编译器回退到零初始化num3

  2. @rustyx 的回答在这里

    data_t d3 = {0}列表初始化语法,它与诸如 之类的聚合data_t一起执行聚合初始化:提供的0值用于初始化第一个成员,其余成员使用相应的默认值进行初始化,如果不存在,则进行值初始化强调我的,针对 C++14 进行了编辑):

    如果初始化器子句的数量小于成员的数量或初始化器列表完全为空,则其余成员将由其默认成员初始化器(如果在类定义中提供)进行初始化,否则将根据通常的列表由空列表进行初始化-初始化规则(使用默认构造函数对非类类型和非聚合类执行值初始化,并对聚合执行聚合初始化)。如果引用类型的成员是这些剩余成员之一,则程序格式错误。

    值初始化意味着非类类型的零初始化。num3就是为什么没有默认值的member 获取 value 的原因0

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

    ...

正如这里所解释的: https: //en.cppreference.com/w/cpp/language/zero_initialization,您也可以这样做:

data_t d{}
Run Code Online (Sandbox Code Playgroud)

在上面的例子中,这段代码:

myStruct _m1{};
Run Code Online (Sandbox Code Playgroud)

...将产生与我上面显示的相同的输出。

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

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);
Run Code Online (Sandbox Code Playgroud)

...输出仍然不是我最初预期的,因为它只将第一个值设置为零!(我不明白为什么;由于列表/聚合初始化值初始化的层次结构(当未设置显式默认值时零初始化);请参阅此处的问题和那里的几个答案):

// Does NOT do what I originally expected! Only sets the FIRST value in the 
// struct to zero! 
// The rest use default values, which are either 1) the explicit defaults if you 
// set them with `= some_num` or whatever in the struct definition, or which are
// 2) in C++ at least, the implicit default values of `0` if you did not set 
// explicit defaults.
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);
Run Code Online (Sandbox Code Playgroud)

然而,在 C 风格的数组(不是结构体)上,这些语义工作得很好。请参阅此处的答案(How to将数组的所有成员初始化为相同的值?)。因此,在使用 C++ 时,以下几行都将 C 样式数组的所有元素设置为零:

d3.num1 = 0
d3.num2 = -100
d3.num3 = 0
d3.num4 = 150
Run Code Online (Sandbox Code Playgroud)

因此,经过多次实验,看起来以下几种方法是对结构体 PERIOD 进行零初始化的唯一方法。如果您有不同的看法,请在此发表评论和/或留下您自己的答案。

在 C++ 中对结构体进行零初始化的唯一方法是:

  1. 明确:

     // 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};
    
    Run Code Online (Sandbox Code Playgroud)
  2. 用于memset()强制所有字节为零:

     data_t d3;
     memset(&d3, 0, sizeof(d3));
    
    Run Code Online (Sandbox Code Playgroud)
  3. 首先将所有默认值设置为零:

     // 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};
    
    Run Code Online (Sandbox Code Playgroud)
  4. 为 C++ 结构编写构造函数

     // 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
    
    Run Code Online (Sandbox Code Playgroud)
  5. 使用没有默认值的结构,并根据它创建对象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;
    
    Run Code Online (Sandbox Code Playgroud)
  6. 因此,如果您有一个具有非零默认值的结构,并且您想要将所有值归零,则必须明确执行此操作!这里还有一些方法:

     // 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();
    
    Run Code Online (Sandbox Code Playgroud)

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

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

在上述所有情况下,如果未设置显式默认值,则隐式默认值为零 ( 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 Stack Exchange:初始化结构数组

去做

  1. [ ] 我最近在 C: 中看到了这个my_struct_t my_struct = {{0}};。例如,研究它的作用,以及它是否仅在 C 中有效,而在 C++ 中无效。

    几周后,2024 年 1 月更新:它似乎是 C 中结构或结构中列表的聚合值初始化。我认为在 C 中它适用于结构或结构中列表,但在 C++ 中它可能只适用于结构中的列表。