在main函数启动前和退出后执行代码
看《windows via c/c++》第4章进程时,里面提到了windows函数的入口函数在crtexe.c的__tmainCRTStartup ()中,于是跟踪了一下代码。
重要的流程如下:
各阶段的处理如下:
Sample示例:
#include <stdio.h> // 本文件所提及的参考文件都位于目录Microsoft Visual Studio 9.0\VC\crt\src\ // 主要包括: // crtexe.c __tmainCRTStartup初始化、函数入口、退出时清理工作 // ctr0init.c 初始化相关 // crt0dat.c 退出相关 // Visual C++编译器将global initializer编译到.CRT$XCU section. // 使用dumpbin /HEADERS xxx.obj 可看到.CRT$XCU // 链接器在链接各个目标文件的.CRT sections时,会按照字母序重排, // 各个目标文件中的.CRT$XCU会被合并到一起,且在.CRT$XCA-.CRT$XCZ之间。 // 所以使用多个全局C++对象时,其初始化顺序是没有标准的。 // C++对象的析构函数并不在.CRT$XP*或.CRT$XT*中,而是在__onexit列表中 class GlobalCppObject { public: GlobalCppObject() { printf("++++++GlobalCppObject constructor\n\n"); } ~GlobalCppObject() { printf("------GlobalCppObject destructor\n\n"); } }; GlobalCppObject g_cppobj; int my_global_var_initializer() { printf("my_global_var_initializer() is called.\n\n"); return 0; } // 这个也是global initializer,也会被编译器放入.CRT$XCU section. int g_foo = my_global_var_initializer(); int my_c_init_function() { printf("my_c_init_function() is called.\n\n"); return 0; } // 构造.CRT$XI* section, 参考crt0dat.c中.CRT$XIA的定义 // 注意有些.CRT$XI*的section已经被系统占用了,具体查看sect_attribs.h typedef int (__cdecl *_PIFV)(void); #pragma section(".CRT$XIX",long,read) __declspec(allocate(".CRT$XIX")) _PIFV __xi_x[] = { my_c_init_function }; void my_exit_fun1() { printf("my_exit_fun1() is called.\n\n"); } void my_exit_fun2() { printf("my_exit_fun2() is called.\n\n"); } void my_exit_fun3() { printf("my_exit_fun3() is called.\n\n"); } int main() { // atexit将void (*pf)()类型的函数注册到__onexit列表中 // __onexit列表是逆序执行的。类似栈的先进后出。 // 全局C++对象的析构函数也在__onexit列表里面, // 且在使用atexit()注册的函数之前 // // crt0dat.c文件第-589行的代码片段: // /* cache the function to call. */ // function_to_call = (_PVFV)_decode_pointer(*onexitend); // // /* mark the function pointer as visited. */ // *onexitend = (_PVFV)_encoded_null(); // // /* call the function, which can eventually change __onexitbegin and __onexitend */ // (*function_to_call)(); atexit(my_exit_fun1); atexit(my_exit_fun2); atexit(my_exit_fun3); printf("---------------------------------------\n"); printf("main() is called.\n"); printf("---------------------------------------\n\n"); return 0; }
运行结果如下:
使用VS自带命令行工具(visual studio 2008 命令提示),加入dumpbin XXX.obj,可看到多了两个section,”.CRT$XCU”和”.CRT$XIU”。
需要重点记住的是:
1、全局C++对象构造函数在.CRT$XCU section中,析构函数在_onexit/atexit table中。
2、编译器为每个.obj文件编译一个.CRT$XCU section,链接器在链接多个文件时会合并.CRT$XCU,所以不同文件间使用多个全局C++对象时,其初始化顺序不是用户代码能控制的。
参考资料:
- 《windows via C/C++》 Chapter 4: Processes
- CRT Initialization
It was the best of times, it was the worst of times, it was the age of wisdom, it was the age of foolishness, it was the epoch of belief, it was the epoch of incredulity. We had everything before us, we had nothing before us.
这是最好的时代,这是最坏的时代;这是智慧的年代,这是愚蠢的年代;这是信仰的时期,这是怀疑的时期;我们的前途拥有一切,我们的前途一无所有。
这是最好的时代,这是最坏的时代;这是智慧的年代,这是愚蠢的年代;这是信仰的时期,这是怀疑的时期;我们的前途拥有一切,我们的前途一无所有。