初始化系列之零初始化(zero initialization)

https://zh.cppreference.com/w/cpp/language/zero_initialization
https://en.cppreference.com/w/cpp/language/zero_initialization

零初始化

把一个对象的初始化值设置为0

说明

零初始化会在下面的情况下执行:

  1. 对于任何static或者thread-local变量,没有使用const,在任何初始化之前,会进行0初始化,也就是static和thread-local修饰的变量会0初始化

  2. 如果不是类,作为数值初始化序列的一部分;如果是类,并且没有构造函数,作为数值初始化中的一员,都会进行零初始化。包括没有提供初始化器的元素聚合数值初始化。参考下面例子

class A {
public:
    int i;
};
class B {
public:
    B() {}
    int i;
};
int main() {
    int t1{};
    cout << t1 << endl;
    int t2 = {};
    cout << t2 << endl;
    int* t3 = new int();
    cout << *t3 << endl;
    int* t4 = new int{};
    cout << *t4 << endl;
    int t5[2] = {};
    cout << t5[0] << "|" << t5[1] << endl;
    int t6[2]{};
    cout << t6[0] << "|" << t6[1] << endl;
    int* t7 = new int[2]();
    cout << t7[0] << "|" << t7[1] << endl;
    int* t8 = new int[2]{};
    cout << t8[0] << "|" << t8[1] << endl;
    A a1;
    cout << a1.i << endl;
    A a2{};
    cout << a2.i << endl;
    A* a3 = new A();
    cout << a3->i << endl;
    A* a4 = new A{};
    cout << a4->i << endl;
    B b1;
    cout << b1.i << endl;
    B b2{};
    cout << b2.i << endl;
    B* b3 = new B();
    cout << b3->i << endl;
    B* b4 = new B{};
    cout << b4->i << endl;
    return 1;
};

除了最后四个结果是未知,剩下的所有的结果都是0,B相关的之所以是未知,就是因为B提供了用户的构造函数,所以B内的成员不会进行零初始化。

  1. 如果是一个字符类型的数组,初始化的字符比数组的空间小,剩下的空间会默认初始化为0
char a[10] = "test";

除了开始的4个分别是test,数组a剩下的空间都是0

零初始化的规则:

  • 如果是标准类型,就会初始化为0

  • 如果是非union的类类型,基类和非静态成员初始化为零,所有填充位初始化为零。忽略构造函数

  • 如果是union,第一个非静态的数据初始化为零,填充位初始化为零

  • 如果是数组,所有元素初始化为零

  • 如果是引用,不做任何处理

说明

在非局部初始化中,static和thread-local变量,如果不是const类型,会在其他初始化之前进行零初始化。如果非类类型的变量,也不是局部变量,并且没有初始化器,那么默认初始化不做任何事情,也就是由一开始初始化为0,最后结果并不会修改,还是保持原来的值-零。

零初始化的指针是空指针,即使空指针不是零。

非局部变量

非局部变量,基本上遇到的就是全局变量,写在类、函数之外的变量,总结一句就是会初始化为0,数字是0,字符是,指针是null

所有的有着静态存储期的非局部变量都会在程序起来时初始化,也就是在main函数运行之前(除非你可以延迟初始化)。所有的有着thread-local存储期的非局部变量在线程起来时初始化,顺序在执行线程函数之前。对于这两种变量,有着两个初始化阶段:

静态初始化

静态初始化有两种形势:

  1. 如果是左值,就进行常量初始化
  2. 否则,非局部的static和thread-local进行零初始化

实际情况:

  • 常量初始化经常在编译期进行。将提前计算的对象作为程序的一部分存储起来。如果编译器没有这样做,那也必须保证初始化在动态初始化之前。
  • 用来零初始化的数据保存在程序的.bss段,不占用硬盘空间,在系统加载程序的时候设置为0
class A {
public:
    int i;
};
class B {
public:
    B() {}
    int i;
};
enum C
{
    C1 = 10,
    C2
};
int t;
int a[2];
A a1;
B b1;
C c;
int* p;
int main() {
    cout << t << endl;
    cout << a[0] << "|" << a[1] << endl;
    cout << a1.i << endl;
    cout << b1.i << endl;
    cout << c << endl;
    cout << p << endl;
    return 1;
};

上面的输出结果全是0,因为对于非局部变量,比如上面的全局变量,在初始化之前会全部初始化为0,并且后续默认初始化什么都不做,所以最终结果是0.

版权声明:本文版权归作者所有,如需转载,请标明出处

原文地址:https://www.cnblogs.com/studywithallofyou/p/14548250.html