初始化系列之零初始化(zero initialization)
https://zh.cppreference.com/w/cpp/language/zero_initialization
https://en.cppreference.com/w/cpp/language/zero_initialization
零初始化
把一个对象的初始化值设置为0
说明
零初始化会在下面的情况下执行:
-
对于任何static或者thread-local变量,没有使用const,在任何初始化之前,会进行0初始化,也就是static和thread-local修饰的变量会0初始化
-
如果不是类,作为数值初始化序列的一部分;如果是类,并且没有构造函数,作为数值初始化中的一员,都会进行零初始化。包括没有提供初始化器的元素聚合数值初始化。参考下面例子
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内的成员不会进行零初始化。
- 如果是一个字符类型的数组,初始化的字符比数组的空间小,剩下的空间会默认初始化为0
char a[10] = "test";
除了开始的4个分别是test,数组a剩下的空间都是0
零初始化的规则:
-
如果是标准类型,就会初始化为0
-
如果是非union的类类型,基类和非静态成员初始化为零,所有填充位初始化为零。忽略构造函数
-
如果是union,第一个非静态的数据初始化为零,填充位初始化为零
-
如果是数组,所有元素初始化为零
-
如果是引用,不做任何处理
说明
在非局部初始化中,static和thread-local变量,如果不是const类型,会在其他初始化之前进行零初始化。如果非类类型的变量,也不是局部变量,并且没有初始化器,那么默认初始化不做任何事情,也就是由一开始初始化为0,最后结果并不会修改,还是保持原来的值-零。
零初始化的指针是空指针,即使空指针不是零。
非局部变量
非局部变量,基本上遇到的就是全局变量,写在类、函数之外的变量,总结一句就是会初始化为0,数字是0,字符是\0,指针是null
所有的有着静态存储期的非局部变量都会在程序起来时初始化,也就是在main函数运行之前(除非你可以延迟初始化)。所有的有着thread-local存储期的非局部变量在线程起来时初始化,顺序在执行线程函数之前。对于这两种变量,有着两个初始化阶段:
静态初始化
静态初始化有两种形势:
- 如果是左值,就进行常量初始化
- 否则,非局部的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.