是否未明确指定未初始化的数据行为?

时间:2011-07-26 02:09:48

标签: c++ initialization

注意:我正在使用g ++编译器(我听说这很好,并且应该非常接近标准)。


我有一个我能想到的最简单的课程:

class BaseClass  {
  public:
    int pub;
};

然后我有三个同样简单的程序来创建BaseClass个对象并打印出他们数据的[未初始化]值。


案例1

BaseClass B1;
cout<<"B1.pub = "<<B1.pub<<endl;

打印出来:

B1.pub = 1629556548

哪个好。我实际上认为它会初始化为零,因为它是POD或Plain Old Datatype或类似的东西,但我想不是吗?到目前为止一切都很好。


案例2

BaseClass B1;
cout<<"B1.pub = "<<B1.pub<<endl;
BaseClass B2;
cout<<"B2.pub = "<<B2.pub<<endl;

打印出来:

B1.pub = 1629556548
B2.pub = 0

这绝对是奇怪的。我以相同的方式创建了两个相同的对象。一个被初始化而另一个没有。


案例3

BaseClass B1;
cout<<"B1.pub = "<<B1.pub<<endl;
BaseClass B2;
cout<<"B2.pub = "<<B2.pub<<endl;
BaseClass* pB3 = new BaseClass;
cout<<"B3.pub = "<<pB3->pub<<endl;

打印出来:

B1.pub = 0
B2.pub = 0
B3.pub = 0

这是最奇怪的。它们都被初始化为零。我所做的只是添加两行代码,它改变了以前的行为。


这只是“未初始化的数据会导致未指明的行为”的情况,还是“引擎盖下”更合乎逻辑?

我真的想了解默认的构造函数/析构函数行为,因为我觉得完全理解继承的东西非常重要。

7 个答案:

答案 0 :(得分:12)

  

这只是“未初始化数据导致未指明行为”的案例

...是

有时,如果您拨打malloc(或调用new的{​​{1}}),您将获得充满零的数据,因为它位于内核的新页面中。其他时候它将充满垃圾。如果你把一些东西放在堆栈上(即自动存储),你几乎肯定会得到垃圾 - 但它很难调试,因为在你的系统上,垃圾可能有点可预测。对于堆栈中的对象,您会发现在完全不同的源文件中更改代码可以更改您在未初始化的数据结构中看到的值。

关于POD: POD是否真的是一个红色的鲱鱼。我只解释了它,因为这个问题提到了POD,并且谈话从那里脱轨了。两个相关概念是存储持续时间和构造函数。 POD对象没有构造函数,但是没有构造函数的所有东西都不是POD。 (从技术上讲,POD对象没有非平凡的构造函数,也没有非平凡构造函数的成员。)

存储时间:有三种。 静态持续时间适用于全局变量,自动适用于局部变量,动态适用于堆上的对象。 (这是一种简化,并不完全正确,但如果您需要完全正确的东西,您可以自己阅读C ++标准。)

任何具有静态存储持续时间的内容都会初始化为零。因此,如果您创建malloc的全局实例,则其BaseClass成员将为零(首先)。由于您将它放在堆栈和堆上,因此该规则不适用 - 并且您没有执行任何其他操作来初始化它,因此它未初始化。它碰巧包含了最后一段代码留在内存中的垃圾。

通常,堆或堆栈上的任何POD都将是未初始化的,除非您自己初始化它,并且值将是未定义的,当您重新编译或再次运行程序时可能会更改。通常,任何全局POD都将初始化为零,除非您将其初始化为其他内容。

检测未初始化的值:尝试使用Valgrind的memcheck工具,它可以帮助您找到使用未初始化值的位置 - 这些通常是错误。

答案 1 :(得分:2)

在所有三种情况下,这些POD对象都可能具有不确定的值。

没有任何初始值设定项的POD对象不会默认值初始化。它们只包含垃圾。

从Standard 8.5 Initializers,

  

“如果没有为对象指定初始化程序,并且该对象是   (可能是cv限定的)非POD类类型(或其数组),.   对象应默认初始化;如果对象是   const-qualified类型,底层类类型应具有   用户声明的默认构造函数。 否则,如果没有初始化程序   为非静态对象,对象及其子对象指定,如果   任何,具有不确定的初始值;如果对象或任何   它的子对象是const限定类型,程序是   病态的“。

您可以将POD结构的所有成员初始化为零,

BaseClass object={0};

答案 2 :(得分:2)

这取决于你如何声明它们:

// Assuming the POD type's
class BaseClass
{
  public:
    int pub;
};

静态存储持续时间对象

这些对象始终为零初始化。

// Static storage duration objects:
// PODS are zero initialized.

BaseClass    global; // Zero initialized pub = 0

void plop()
{
    static BaseClass functionStatic;   // Zero initialized.
}

自动/动态存储持续时间对象

默认情况下这些对象可能会初始化为零,具体取决于您如何声明它们

void plop1()
{
    // Dynamic
    BaseClass*  dynaObj1   = new BaseClass;   // Default initialized (does nothing)
    BaseClass*  dynaObj2   = new BaseClass(); // Zero Initialized

    // Automatic
    BaseClass   autoObj1;                     // Default initialized (does nothing)
    BaseClass   autoObj2   =     BaseClass(); // Zero Initialized

    // Notice that zero initialization of an automatic object is not the same
    // as the zero initialization of a dynamic object this is because of the most
    // vexing parse problem

    BaseClass    autoObj3(); // Unfortunately not a zero initialized object.
                             // Its a forward declaration of a function.
}

我使用术语“零初始化”/“默认初始化”但技术上稍微复杂一些。 “默认初始化”将成为pub成员的“无初始化”。而()调用“值初始化”,成为pub成员的“零初始化”。

注意:由于BaseClass是一个POD,这个类的行为就像内置类型一样。如果您将BaseClass替换为任何标准类型,则行为是相同的。

答案 3 :(得分:1)

你编写类的方式,pub的值是未定义的,可以是任何东西。如果你创建一个默认构造函数来调用pub的默认构造函数 - 它将保证为零:

class BaseClass  {
  public:
    BaseClass() : pub() {}; // calling pub() guarantees it to be zero.
    int pub;
};

这将是一个更好的做法。

答案 4 :(得分:0)

作为一般规则,是的,未初始化的数据会导致未指定的行为。这就是为什么像C#这样的其他语言会采取措施确保您不使用未初始化的数据。

答案 5 :(得分:0)

这就是为什么你始终总是将类(或任何变量)初始化为稳定状态而不是依靠编译器为你做的事情;特别是因为一些编译器故意用垃圾填充它们。实际上,唯一的POD MSVC ++没有填充垃圾bool,它们被初始化为true。人们会认为将它伪装成假是更安全的,但那是微软的。

答案 6 :(得分:0)

案例1

无论封装类型是否为POD,内置类型的数据成员都不会被封装的默认构造函数默认初始化。

案例2

不,都没有初始化。其中一个的内存位置的基础字节刚刚发生0

案例3

相同。

您似乎期待对未初始化对象的“价值”提供一些保证,同时宣称不存在这样的价值的理解。