在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++对象时,其初始化顺序不是用户代码能控制的。

 

参考资料:

  1. 《windows via C/C++》 Chapter 4: Processes
  2. CRT Initialization

 

posted @ 2014-04-22 16:28  shokey520  阅读(947)  评论(0编辑  收藏  举报