cpp 中全局变量和 static 变量初始化问题
一、初始化时机
- 全局变量、文件域中的静态变量、类中的成员静态变量在 main 函数执行前初始化
- 局部变量中的静态变量在第一次调用时初始化
局部静态变量,C 和 C++ 的区别:
- 在 C 语言中是编译期间初始化并分配内存,因此不能用变量给静态局部变量赋值,只能用常量
- 在 C++ 语言中是第一次执行时初始化,因为 C++ 引入了对象的概念,对象一般需要构造函数,无法简单的分配内存,因此可以用变量赋值,并且在第一次使用时初始化
初始化分为静态初始化、动态初始化
静态初始化,用常量来对全局变量进行初始化的情况。根据变量是否设置初始值分别放于 data segment 段(设置初值)和 bss 段(未设置初始值)
1
int a = 3;
动态初始化(运行期)(main 函数前,局部静态变量除外)
1
2
3
4
5# 需要经过函数调用来完成的初始化
int a = foo()
# 复杂类型的初始化。需要调用构造函数。
class A { A() {} }
A aa;静态初始化的时机是先于动态初始化的
注意:
c++ 规定,非局部 static 变量的初始化发生在 main 函数执行之前,也即 main 函数之前的单线程启动阶段,所以不存在线程安全问题。但 c++ 没有规定多个非局部 static 对象的初始化顺序,尤其是来自于多个编译单元的非局部 static 对象,他们的初始化顺序是随机的,不过可以设置优先级
局部静态变量,其初始化发生在控制流第一次执行到该对象的初始化语句时。多个线程的控制流可能同时到达其初始化语句。
在C++11之前,在多线程环境下局部静态对象的初始化并不是线程安全的。具体表现就是:如果一个线程正在执行局部静态对象的初始化语句但还没有完成初始化,此时若其它线程也执行到该语句,那么这个线程会认为自己是第一次执行该语句并进入该local static对象的构造函数中。这会造成这个local static对象的重复构造,进而产生内存泄露问题。所以,local static对象在多线程环境下的重复构造问题是需要解决的。
而C++11则在语言规范中解决了这个问题。C++11规定,在一个线程开始local static 对象的初始化后到完成初始化前,其他线程执行到这个local static对象的初始化语句就会等待,直到该local static 对象初始化完成。
二、初始化顺序
对于编译单元(同一个文件)的全局变量来讲,初始化顺序跟声明的顺序一致。销毁顺序则相反
对于不同编译单元的全局变量,初始化顺序不确定。对于不同编译单元的全局变量互相引用的情况应避免
三、解决不同文件相互引用全局变量初始化顺序不确定问题
可以通过函数调用,引用的时候不直接引用全局变量,而是放在一个函数中。函数中的全局变量在调用时初始化。
1 | int get_a() { |