C++结构体初始化的正确方法

108

我们的代码涉及一个POD(Plain Old Datastructure)结构体(它是一个基本的C++结构体,其中包含其他结构体和需要在开始时初始化的POD变量。)

根据我所阅读的内容,似乎:

myStruct = (MyStruct*)calloc(1, sizeof(MyStruct));

应该将所有值初始化为零,就像这样:

myStruct = new MyStruct();

然而,当结构以第二种方式初始化时,Valgrind随后会在使用这些变量时抱怨“条件跳转或移动取决于未初始化的值”。我在这里的理解有误,还是Valgrind给出了错误的提示?


相关常见问题解答:https://dev59.com/_3RB5IYBdhLWcg3wcm6d#620402 - Robᵩ
据我所知,您不应该收到那个错误消息。也许您的数据结构不是一个POD?您能否将代码减少到最小程序,仍然展示出这种行为,并发布这个提炼出来的程序?(请参见http://sscce.org)。 - Robᵩ
1
什么编译器?你能发布一下MyStruct的定义吗? - Alan Stokes
啊,我现在明白你最初的意思了。 - Johannes Schaub - litb
这个程序符合您的描述,但在Ubuntu LTS 10.04.2上,不会产生您所描述的错误。 - Robᵩ
7个回答

153

C++中,类和结构体在初始化方面是相同的。

非POD结构体可以拥有构造函数以初始化成员变量。
如果结构体是POD类型,则可以使用一个初始值设定项。

struct C
{
    int x; 
    int y;
};

C  c = {0}; // Zero initialize POD

或者您可以使用默认构造函数。

C  c = C();      // Zero initialize using default constructor
C  c{};          // Latest versions accept this syntax.
C* c = new C();  // Zero initialize a dynamically allocated object.

// Note the difference between the above and the initialize version of the constructor.
// Note: All above comments apply to POD structures.
C  c;            // members are random
C* c = new C;    // members are random (more officially undefined).

我认为valgrind在抱怨是因为这正是C++以前的工作方式。(我不确定C++何时使用零初始化默认构造进行升级)。你最好添加一个初始化对象的构造函数(结构体允许构造函数)。

顺便说一句:
很多初学者尝试进行值初始化:

C c(); // Unfortunately this is not a variable declaration.
C c{}; // This syntax was added to overcome this confusion.

// The correct way to do this is:
C c = C();

如果您搜索“最令人困惑的解析”,就会提供比我更好的解释。


1
你有C++中对变量进行零初始化的参考吗?据我所知,它仍然具有与C相同的行为。如果我没记错的话,微软的编译器甚至在某个时候放弃了对未初始化成员进行零初始化的操作(出于性能原因,我认为),这显然给其他微软部门带来了相当严重的问题 :) - Marc Mutz - mmutz
1
不,不是 C(),但看起来你是对的 - Marc Mutz - mmutz
4
对于一个POD类TT()始终会将标量子对象进行零初始化。在C++98中,顶层初始化的名称与C++03不同("默认初始化"与"值初始化"),但是对于POD而言,其最终效果是相同的。无论是C++98还是C++03都会使得其中所有值都为零。 - Johannes Schaub - litb
2
@RockyRaccoon C c(); 不幸的是这不是一个变量声明,而是一个函数的前向声明(一个名为'c'的函数,它不带参数并返回类型为C的对象)。谷歌:最令人困惑的解析。 - Martin York
1
@RockyRaccoon 这里和你在评论中链接的问题不同之处在于,这种情况下构造函数中没有使用参数。因此编译器很难解析这个区别变量声明和函数的前向声明。它选择了函数的前向声明。如果您需要在构造函数中使用参数,则编译器可以轻松地看到这是一个变量声明而没有任何问题。C c(4); 是一个有效的变量声明。 - Martin York
显示剩余14条评论

4
我写了一些测试代码:

#include <string>
#include <iostream>
#include <stdio.h>

using namespace std;

struct sc {
    int x;
    string y;
    int* z;
};

int main(int argc, char** argv)
{
   int* r = new int[128];
   for(int i = 0; i < 128; i++ ) {
        r[i] = i+32;
   }
   cout << r[100] << endl;
   delete r;

   sc* a = new sc;
   sc* aa = new sc[2];
   sc* b = new sc();
   sc* ba = new sc[2]();

   cout << "az:" << a->z << endl;
   cout << "bz:" << b->z << endl;
   cout << "a:" << a->x << " y" << a->y << "end" << endl;
   cout << "b:" << b->x << " y" << b->y <<  "end" <<endl;
   cout << "aa:" << aa->x << " y" << aa->y <<  "end" <<endl;
   cout << "ba:" << ba->x << " y" << ba->y <<  "end" <<endl;
}

使用g++编译并运行:

./a.out 
132
az:0x2b0000002a
bz:0
a:854191480 yend
b:0 yend
aa:854190968 yend
ba:0 yend

好的,我对 new sc;new sc(); 不是很熟悉。我本以为缺少括号是一个错误。但这似乎证明了答案是正确的(当使用默认构造函数时,它确实初始化为0)。 - nycynik
一个很好的例子! - John

2
根据您提供的信息,似乎是valgrind中的误报。使用()new语法应该会将对象进行值初始化,假设它是POD类型。
有可能您的结构体中的某个子部分不是POD类型,这就阻止了预期的初始化。您是否能够简化代码并将其变成一个可贴示例,以便查看valgrind错误?
或者,也有可能是编译器没有真正地对POD结构进行值初始化。
无论如何,最简单的解决方案可能是根据需要为结构体/子部分编写构造函数。

0

既然这是一个POD结构体,你可以随时将其memset为0 - 这可能是获取字段初始化的最简单方法(假设这是合适的)。


1
无论是结构体还是类,都与能否使用memset或创建构造函数无关。请参见例如https://dev59.com/ZnE85IYBdhLWcg3wgDlI - Erik
通常情况下,在创建类之后,您不会使用memset对其进行初始化,因为它的构造函数(可能)已经完成了初始化。 - Scott C Wilson
2
在C++中,“class”和“struct”之间没有区别,除了默认保护(分别为private和public)。结构体可以是非POD,类可以是POD,这些概念是正交的。 - Marc Mutz - mmutz
@mmutz和@Erik - 同意了 - 缩短了我的答案以避免混淆。 - Scott C Wilson
memset() 不是最好的选择,你可以通过调用其默认构造函数对 POD 结构进行零初始化。 - Nils

0
    You can declare and initalise structure in C++ this way also:::
    
    struct person{
        int a,h;
     
        person(int a1,int h1): a(a1),h(h1){
            
        }// overriden methods

        person():a(0),h(0){
            
        }// by default
    };

   struct person p; 
   --> This creates from by default Person Age: 0 height: 0

   struct person p = person(3,33);  
    --> This creates from overriden methods Person Age: 3 height: 33
     

     

person():a(0),h(0) --> field(value)人物():a(0),h(0) --> 字段(值) - Bhanu Mahto

0

在我看来,这似乎是最简单的方法。可以使用花括号“{}”初始化结构成员。例如,以下是有效的初始化。

struct Point 
{ 
   int x, y; 
};  

int main() 
{ 
   // A valid initialization. member x gets value 0 and y 
   // gets value 1.  The order of declaration is followed. 
   struct Point p1 = {0, 1};  
}

关于C++中的结构体有很好的信息 - https://www.geeksforgeeks.org/structures-in-cpp/


-1
你需要初始化你在结构体中拥有的任何成员,例如:
struct MyStruct {
  private:
    int someInt_;
    float someFloat_;

  public:
    MyStruct(): someInt_(0), someFloat_(1.0) {} // Initializer list will set appropriate values

};

在这种情况下,我不是使用类,而是使用结构体。 - KC3BZU
结构体是一个类。编辑了我的帖子 :) - ralphtheninja
3
你不需要为 POD 这样做。 - Alan Stokes
结构体如何拥有私有成员? - user3091673
你是什么意思怎么做?只需在它们前面加上private,就像普通的类一样。 - ralphtheninja

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