默认初始化与零初始化

Posted

技术标签:

【中文标题】默认初始化与零初始化【英文标题】:Default Initialization Versus Zero Initialization 【发布时间】:2014-12-15 12:48:11 【问题描述】:

我无法理解 gcc 4.8.1 或 Visual Studio 2015 在默认初始化与值初始化方面的行为。

我自己试图理解这些之间的差异并可能遇到编译器错误并没有帮助?

我的问题是:有人可以解释这种行为吗?最好告诉我应该发生什么。

我有两个班级:

class Foo
    int _bar;
public:
    void printBar() cout << _bar << endl; 
;

class bar
    int ent;
public:
    int getEnt()return ent;
;

我正在使用以下代码进行测试:

int main()

    Foo foo;

    foo.printBar();
    Foo().printBar();

    bar b;

    cout << b.getEnt() << endl;

    return 0;

在 gcc 和 Visual Studio 上我得到:

134514795 0 0

现在如果我将测试代码更改为:

int main()

    Foo foo;

    foo.printBar();

    bar b;

    cout << b.getEnt() << endl;

    return 0;

gcc 给我:

0 0

Visual Studio 给了我:

50790236 51005888

【问题讨论】:

如果有人感兴趣,我开始看这个以回应:***.com/a/27443703/2642059 这个问题不一样吗? @AntonSavin 我真的只想知道编译器应该对初始化做什么。我不认为这是同一个问题? 在调试版本中,通常使用填充符来帮助检测未初始化的值...而其他字节用于帮助检测其他一些与内存相关的问题(例如已释放的内存)。 【参考方案1】:

像这样没有用户定义构造函数的类的默认初始化,什么都不做,给每个平凡的成员留下一个不确定的值。

值初始化将对每个成员进行零初始化。

在第一种情况下,您正在打印:

默认初始化Foo foo; 的不确定值 值初始化的零值Foo() 默认初始化bar b; 的不确定值

第三个恰好为零;可能是因为它重用了临时值初始化Foo 的存储。

在第二种情况下,您正在打印两个默认初始化对象的不确定值。巧合的是,它们在一种情况下具有零值,但在另一种情况下则没有。

两个程序都有未定义的行为,因为它们使用未初始化的值。

【讨论】:

【参考方案2】:
Foo foo;

这个默认初始化foo,由于Foo的默认构造函数是微不足道的,它实际上根本没有初始化它,所以foo._bar可以保存任何值(包括0).

Foo()

这个值初始化临时对象,在平凡的默认构造函数的情况下意味着零初始化,所以Foo()._bar等于0。

【讨论】:

对不起,如果这有点迂腐,但你绝对不希望像 struct String std::string str;; 这样的类因为它没有用户提供的默认构造函数而进行零初始化。【参考方案3】:

逻辑很简单:

    类的默认初始化只是默认初始化所有成员。 内置类型的默认初始化使成员未初始化。 访问未初始化的对象会产生undefined behavior。 未定义的行为可以为所欲为。 两个编译器都提供“正确”的结果。请注意,导致发出 nasal demons 也是正确的。

【讨论】:

【参考方案4】:

n3376 个引号

8.5/11

如果没有为对象指定初始化器,则该对象为 default-initialized; 如果不执行初始化,则对象为 自动或动态存储期限具有不确定的价值。 [ 笔记: 具有静态或线程存储持续时间的对象是零初始化的, 见 3.6.2。 ——尾注]

8.5/6

默认初始化 T 类型的对象意味着:如果 T 是一个(可能 cv-qualified) 类类型(第 9 条),T 的默认构造函数是 调用(如果 T 没有可访问的,则初始化格式错误 默认构造函数);

8.5/10

一个对象,其初始值设定项是一组空括号,即 (), 应该是值初始化的。

8.5/7

对 T 类型的对象进行值初始化意味着:

...

否则,对象被零初始化。

8.5/5

对 T 类型的对象或引用进行零初始化意味着:如果 T 是 (可能是 cv 限定的)非联合类类型,每个非静态数据 成员和每个基类子对象都是零初始化和填充 初始化为零位;

因此,在您的情况下,既没有静态存储持续时间变量,也没有线程局部变量,因此对象 foob 将被默认初始化,这意味着将调用该构造函数。默认构造函数(不是用户定义的)不会初始化成员,并且在成员中将是任意垃圾,这个任意垃圾可能是 0(感谢 Jarod42 在评论中指出这一点)。 而Foo().printBar(); 应该打印 0,因为对象是零初始化的。

【讨论】:

任意垃圾可能是0 值得一提的是,Foo().printBar(); 必须打印 0(至少单独打印,而不是在程序中有其他 UB 时)。 @Jarod42 这实际上是一个非常好的评论。我没有考虑过默认初始化可能会产生 0 结果。嗯……必须有办法处理这个问题,这样我才能真正看到发生了什么。

以上是关于默认初始化与零初始化的主要内容,如果未能解决你的问题,请参考以下文章

差分方程的零输入响应与零状态响应

默认初始化值初始化

java成员变量怎么默认初始化

数组动态初始化时,数组元素会被赋予一个默认值,简述各数据类型的初始值?

初始化

默认初始化