您的位置:首页 > 职场人生

程序员的自我修养: 全局构造/析构函数是怎样调用的?

2010-03-11 10:50 323 查看
全局构造/析构函数与C/C++运行库

主要以MSVC CRT来说:

大致的过程如下:

mainCRTStartup()
{
...
_initterm(__xc_a, __xc_z);
}

typedef void (__cdecl * _PVFV)();

static void __cdecl _initterm(_PVFV* pfbegin, _PVFV* pfend)
{
while(pfbegin < pfend)
{
if(*pfbegin != NULL)
(**pfbegin)();
++pfbegin;
}
}

//__xc_a, __xc_z的定义
_CRTALLOC(".CRT$XCA") _PVFV __xc_a[] = {NULL};
_CRTALLOC(".CRT$XCZ") _PVFV __xc_z[] = {NULL};

//_CRTALLOC的定义
#pragma section(".CRT$XCA", long, read)
#pragma section(".CRT$XCZ", long, read)
...
#define _CRTALLOC(x) __declspec(allocate(x))


__xc_a, __xc_z分别处于两个特殊的段里, 并具有long,read属性.
因此这两条pragma指令实际在obj文件里生成了名为.CRT$XCA和.CRT$XCAZ的两个段.
__declspec(allocate(x))将其分别分配到对应段中

当链接的时候,链接器会将所有相同属性的段合并,注意的是,合并到输出段时,是根据字符表顺序依次排序.
由于.CRT$XC*这些段的属性都是只读的,且名字很相近,最后往往被放到只读段中,成为.rdata的一部分.
这样就形成了存储所有全局初始化函数的地址的数组.

还有:.CRT$XC*段中存放的是该obj文件的全局初始化函数的地址.所以最后形成的是一个函数地址数组.

#include <iostream>
using namespace std;

#define SECNAME ".CRT$XCV"
#pragma section(SECNAME, long, read)

void foo()
{
cout << "hello world!" << endl;
}

typedef void (__cdecl * _PVFV)();
__declspec(allocate(SECNAME)) _PVFV dummy[] = {foo, foo};
int main()
{
return 0;
}


结果: Hello world!

MSVC CRT 析构

是当调用初始化函数的时候,用atexit函数注册了析构函数的执行地址.
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: