从汇编看c++中全局对象和全局变量

    添加时间:2013-7-25 点击量:

    先来看c++源码:



    #include <iostream>
    
    using namespace std;
    class X {
    public:
    int i;
    public:
    X(
    int ii = 0) : i(ii) {
    }
    ~X() {}
    };

    X xxx(
    1);//全局对象
    int i = 2;//全局变量

    int main() {
    }


    在代码里面定义了一个全局对象xxx和一个全局变量i,main函数什么也不做。在定义全局对象xxx处打一个断点,然后在vs2010里面调试,查看对象xxx和变量i的内存,如下:
    xxx的内存:



    对象xxx的内存内容都被置为0,而不是无意义的数


    i的内存:



    可以看到,固然i在对象xxx后面定义,然则,当断点打在定义xxx处时,它内存值已经是2了。这是因为具有初始值的全局变量,其值在连接时就被写入了文件。当用户履行该文件的时辰,操纵体系解析文件中的数据,将响应数据写入内存之中。是以,全局变量出生于地点履行文件被操纵体系加载之后,履行第一条代码之前。(main函数并不是法度履行的第一条代码)。


    单步跟进后,我们会看到如下的机关对象xxx的汇编码:



        12: X xxx(1;//全局对象
    
    00BF4450 push ebp
    00BF4451 mov ebp,esp
    00BF4453 sub esp,0C0h
    00BF4459 push ebx
    00BF445A push esi
    00BF445B push edi
    00BF445C lea edi,[ebp-0C0h]
    00BF4462 mov ecx,30h
    00BF4467 mov eax,0CCCCCCCCh
    00BF446C rep stos dword ptr es:[edi]
    00BF446E push 1 ;压入参数1
    00BF4470 mov ecx,offset xxx ;获取对象xxx的首地址
    00BF4475 call X::X ;调用对象xxx的机关函数
    00BF447A push offset `dynamic atexit destructor for xxx ;获取对象xxx的析构函数地址 ,传递给atexit函数
    00BF447F call @ILT+100(_atexit) (0BF1069h) ;调用atexit函数,注册对象xxx的析构函数
    00BF4484 add esp,4
    00BF4487 pop edi
    00BF4488 pop esi
    00BF4489 pop ebx
    00BF448A add esp,0C0h
    00BF4490 cmp ebp,esp
    00BF4492 call @ILT+305(__RTC_CheckEsp) (0BF1136h)
    00BF4497 mov esp,ebp
    00BF4499 pop ebp
    00BF449A ret


    上方汇编码就是全局对象xxx机关函数(没有源码对比)汇编码,在函数里面,不仅调用了对象xxx的机关函数,并且将xxx的析构函数经由过程调用atexit函数进行了注册。下面就来看看应用法度调用这个函数的过程。


    对于一个应用法度,在调用main函数之前,编译器其实已经做了很多工作,是以,main函数并不是应用法度进口。当应用法度被加载时,进口代码是一个叫mainCRTStartup的函数(经由过程vs2010的调用客栈可以看到),这个函数调用_tmainCRTStartup函数,_tmainCRTStartup函数又调用_initterm函数,它的c++源码如下:



    static void __cdecl _initterm ( _PVFV  pfbegin, _PVFV  pfend)
    
    {
    while ( pfbegin < pfend )
    {

    if ( pfbegin != NULL )
    pfbegin)();
    ++pfbegin;
    }
    }


    此中它的参数里面的_PVFV是一个函数指针数组,编译器为每一个全局对象天活力关函数,而机关函数的地址就存储在这个函数指针数组里。_PVFV的定义原型如下:



    typedef void(_cdecl _PVFV)(void);


    从定义可以看出_PVFV指向的机关函数为一个无参无返回值的函数。因为函数的类型被同一成_PVFV的情势,是以可以经由过程数组同一经管和履行。
    当mian函数履行完毕之后,由exit来停止过程,从而终止法度的进行。全局对象的析构函数的调用也在此中。上方看到,在调用全局对象xxx的机关函数时,将其析构函数经由过程atexit函数进行了注册。是以,退出法度时会以注册相反的次序调用注册的析构函数,并在析构函数中调用全局对象的真正析构函数,道理同调用机关函数类似。

    分享到: