经常性的在面试和笔记题目中会看到全局变量、静态全局变量、静态变量的存储位置和初始化时间的问题,
一般都能答出来他们存储位置,但可能有很多人不清楚这些变量是什么时候初始化的,下面我们从代码角度来看看,这些变量是什么时候初始化的,
如果在main函数之前初始化,那么又是如何做的。
samplecode定义如下:
MyClass g_A; int g_nTest2 = 1; int main() { return 0; }
在main之前还有另外一个函数:(w)mainCRTStartup需要调用(crt的main入口),代码如下:
int mainCRTStartup(void) { /* * The /GS security cookie must be initialized before any exception * handling targetting the current image is registered. No function * using exception handling can be called in the current image until * after __security_init_cookie has been called. */ __security_init_cookie(); return __tmainCRTStartup(); }
然后就是调用__tmainCRTStartup,
__tmainCRTStartup这个函数里会有如下代码来做C++初始化:
/* * do C++ constructors (initializers) specific to this EXE */ if (__native_startup_state == __initializing) { _initterm( __xc_a, __xc_z ); __native_startup_state = __initialized; }
mainret = main(argc, argv, envp);
g_A的构造函数就在_inititerm函数里去调用,具体有兴趣可以去看源码(X:\Program Files\Microsoft Visual Studio 8\VC\crt\src\crtexe.c)
2. 已初始化的 全局变量(静态变量) 那么有人会问,像如下的全局变量什么时候初始化呢:
int g_nTest2 = 1;
答案是这些已知类型的变量在PE文件中已经被硬编码了那个值,g_nTest2被硬编码成了1,
如果要证实可以用调试工具去看下那个地址的值是什么时候写入的。
用OD载入(第一次暂停记得选择在系统断点)为了方便,我选择了如下的测试代码:
int g_nTest2 = 0x001200FE; void TestGolabal() { static int gA2 = 0x003400AB; ... }
系统断点下来,我们会看到,那个全局变量对应的地址早就被赋值了,如下图:
直接打开PE文件,我们也看到了如下的值:
3.未初始化全局变量
未初始化的全局变量gcc编译器中会分配一个 .bss section,在PE被加载时会根据PE header信息分配空间,
并初始化为0,但是VC编译器编译出来的并没有.bss节,未初始化的全局静态量也在.data段中。
总结下,class等这些全局变量是在main函数之前,在__tmainCRTStartup里调用的构造函数进行初始化;
而像int等已知类型的已初始化全局变量和静态变量它的值已经写在PE文件里了,PE Loader加载时就已经写
入,未初始化的全局和静态变量则也是在PE加载时为其分配空间,并初始化成0.具体我们要看下PE加载逻辑了。
对于PE加载逻辑以后整理下。