• <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>

            每天早晨叫醒你的不是鬧鐘,而是夢想

              C++博客 :: 首頁 :: 聯系 :: 聚合  :: 管理
              62 Posts :: 0 Stories :: 5 Comments :: 0 Trackbacks

            常用鏈接

            留言簿(1)

            我參與的團隊

            搜索

            •  

            最新評論

            閱讀排行榜

            評論排行榜

            1、 在介紹靜態對象、全局對象與程序的運行機制之間的關系之前,我們首先看一下atexit函數。

            atexit函數的聲明為:int atexit( void ( __cdecl *func )( void ) );

            參數為函數指針,返回值為整型,0表示成功,其他表示失敗。當程序運行結束時,他調用atexit函數注冊的所有函數。如果多次調用atexit函數,那么系統將以LIFO(last-in-first-out)的方式調用所有的注冊函數。

            舉例如下(代碼摘自MSDN):

             1#include <stdlib.h>
             2#include <stdio.h>
             3 
             4void fn1( void ), fn2( void ), fn3( void ), fn4( void );
             5 
             6void main( void )
             7{
             8  atexit( fn1 );
             9  atexit( fn2 );
            10  atexit( fn3 );
            11  atexit( fn4 );
            12  printf( "This is executed first.\n" );
            13}

            14 
            15void fn1()
            16{
            17  printf( "next.\n" );
            18}

            19 
            20void fn2()
            21{
            22  printf( "executed " );
            23}

            24 
            25void fn3()
            26{
            27  printf( "is " );
            28}

            29 
            30void fn4()
            31{
            32  printf( "This " );
            33}

            34



            編譯、運行程序后,程序的輸出為:
            This is executed first.
            This is executed next.
            注冊函數的順序為:fn1、fn2、fn3、fn4,但是調用順序為fn4、fn3、fn2、fn1。

            2、理解了atexit函數之后,我們就可以來看看局部靜態對象了。

            1 class AAA{ … } ;
            2 AAA* createAAA() 
            3 {
            4         static AAA a ;
            5         return &a ;
            6 }
            7 


            在調試狀態下,匯編代碼如下(請觀察藍色標記出來的代碼):
            AAA* createAAA()
            {
                 …
                 static AAA a ;

            [1] 00401056 call        AAA::AAA (4010A0h)
            [2] 0040105B push      offset `createAAA'::`2'::a::`dynamic atexit destructor' (442410h)
            [3] 00401060 call        atexit (409A50h)
            00401065 add            esp,4
            00401068 mov           dword ptr [ebp-4],0FFFFFFFFh
                 return &a ;
            0040106F mov           eax,offset a (452620h)
            }

            00401091 ret  
            注:[1]、[2]、[3]為方便說明加入的字符,實際代碼中并不存在。
            [1]語句很明顯的調用AAA的構造函數。
            [2]語句將442410h壓入棧中。
            [3]語句調用atexit函數,根據我們的了解,atexit的參數應該是函數指針。那么我們來分析一下442410h處的代碼,從注釋來看,我們看到了destructor。代碼如下:
            `createAAA'::`2'::a::`dynamic atexit destructor':

            [1] 0044242E mov         ecx,offset a (452620h)
            [2] 00442433 call        AAA::~AAA (403A90h)

            0044244B ret           
            [1]語句將a的地址放在ecx寄存器中,這是this調用規范的風格。
            [2]語句調用AAA的析構函數。

            程序結束時,將調用atexit函數注冊的442410h處的函數,進而調用了AAA的析構函數。從而保證了析構函數的調用。

             
            3、   了解了局部靜態對象之后,我們來看看全局對象。
            我們知道全局對象必須在main函數前已經被構造。為了弄清楚全局對象何時被構造,我在全局對象的實例化處設置了斷點,調用堆棧如下:
            static.exe!aaaa::`dynamic initializer'() Line 22 C++
            static.exe!_initterm(void (void)* * pfbegin=0x00451038, void (void)* * pfend=0x00451064) Line 707 C
            static.exe!_cinit(int initFloatingPrecision=1) Line 208 + 0xf bytes C
            static.exe!mainCRTStartup() Line 266 + 0x7 bytes C


            作為對比,我在AAA的析構函數出設置了斷點,調用堆棧如下:
                 static.exe!AAA::~AAA() Line 19  C++
                 static.exe!aaaa::`dynamic atexit destructor'() + 0x28 bytes  C++
                 static.exe!doexit(int code=0, int quick=0, int retcaller=0) Line 451  C
                 static.exe!exit(int code=0) Line 311 + 0xd bytes  C
                 static.exe!mainCRTStartup() Line 289  C


            由此我們可以看出程序的實際入口點位mainCRTStartup而不是main函數(相對于ANSI的控制臺程序而言)。
            我們來分析一下_cinit函數:
            注釋中有一句[3. General C initializer routines],看來該函數的功能之一是完成C的初始化例程。
            函數的核心代碼如下:
            /*
                     * do initializations
                     */
                    initret = _initterm_e( __xi_a, __xi_z );
            /*
                     * do C++ initializations
                     */
                    _initterm( __xc_a, __xc_z );
            看來該函數主要進行C、C++的初始化。我們進一步分析函數_initterm_e和_initterm,兩個函數的功能進本相同,都是遍歷函數指針(由參數指定函數指針的開始位置[__xi_a、__xi_z]、結束位置[__xc_a、__xc_z]),如果函數指針不為null,那么調用該函數。
            那么__xi_a、__xi_z和__xc_a、__xc_z到底代表了什么呢?在cinitexe.c文件中有如下代碼:
            #pragma data_seg(".CRT$XIA")
            _CRTALLOC(".CRT$XIA") _PVFV __xi_a[] = { NULL };
             
            #pragma data_seg(".CRT$XIZ")
            _CRTALLOC(".CRT$XIZ") _PVFV __xi_z[] = { NULL };/* C initializers */
             
            #pragma data_seg(".CRT$XCA")
            _CRTALLOC(".CRT$XCA") _PVFV __xc_a[] = { NULL };
             
            #pragma data_seg(".CRT$XCZ")
            _CRTALLOC(".CRT$XCZ") _PVFV __xc_z[] = { NULL };/* C++ initializers */
            #pragma comment(linker, "/merge:.CRT=.data")
            可以看出這四個變量分別在數據段.CRT$XIA、.CRT$XIZ、.CRT$XCA、.CRT$XCZ中。當連接器布局代碼時,它按根據的名稱,按照字母排序的規則,排列所有段。這樣在段.CRT$XIA中的變量出現在段.CRT$XIZ所有變量之前,從而形成鏈表。對于.CRT$XCA、.CRT$XCZ數據段同理。最后這四個數據段被合并到.data數據段中。
            再看看這些變量的類型,typedef void (__cdecl *_PVFV)(void); 所以這些變量組成了2個初始化函數指針鏈表。
            調試過程中,看到__xc_a、__xc_z鏈表中,指向的初始化函數很多是構造函數,如:
            static std::_Init_locks initlocks;
            static filebuf fout(_cpp_stdout);
            extern _CRTDATA2 ostream cout(&fout);
            cout對象也在此時被構造。
            對于析構函數的調用也是采用相同的方式,只是此時每一種初始化,都有一種終止函數與之對應。

            4、 總結
            l         編譯、連接程序時,編譯器將所有全局對象的初始化函數放入.CRT$Xx中,連接器將所有的.CRT$XCx段合并成為.rdata數據段。在.CRT$XCA 到 .CRT$XCZ的所有段的數據組成初始化函數指針列表。
            l   函數執行時,_initterm( __xc_a, __xc_z )函數調用所有的初始化函數。構造全局對象。構造對象完畢,調用atexit函數來保證析構函數的調用。Modern C++ Design就是通過控制調用atexit函數來決定對象的析構順序的。
            l   對于靜態對象使用atexit來保證析構函數的調用。
            l    程序結束時,調用exit來析構全局對象或靜態對象。


            本文來自CSDN博客,轉載請標明出處:http://blog.csdn.net/coffeemay/archive/2006/11/19/1395250.aspx

            posted on 2011-04-29 11:06 沛沛 閱讀(877) 評論(0)  編輯 收藏 引用 所屬分類: C++
            久久青青草原精品国产不卡| 蜜臀av性久久久久蜜臀aⅴ| 久久久久99精品成人片欧美| 久久精品国产亚洲AV无码麻豆| 久久精品国产久精国产思思| 日本精品久久久久中文字幕| 久久影视综合亚洲| 99国产欧美久久久精品蜜芽 | 久久九九免费高清视频| 久久久精品久久久久久| 久久丫精品国产亚洲av不卡| 国产成人综合久久精品尤物| 亚洲精品国产字幕久久不卡| 99久久精品免费看国产一区二区三区| 久久婷婷五月综合97色直播| 久久精品蜜芽亚洲国产AV| 欧美久久久久久午夜精品| 久久精品国产亚洲AV无码偷窥| 欧美日韩成人精品久久久免费看| 粉嫩小泬无遮挡久久久久久| 午夜精品久久久久久| 精品久久久久久无码国产| 久久九九精品99国产精品| A级毛片无码久久精品免费| 久久黄色视频| 国产午夜电影久久| 91精品国产高清久久久久久国产嫩草 | 日韩人妻无码精品久久久不卡 | 99国产精品久久久久久久成人热| 性做久久久久久久久| 久久九九免费高清视频| 99久久亚洲综合精品成人| 国产精品久久久久久久久免费| 亚洲精品国产字幕久久不卡| 中文字幕无码精品亚洲资源网久久| 久久久久国产日韩精品网站 | 国产成人精品综合久久久| 久久精品国产AV一区二区三区| 中文字幕精品久久久久人妻| 99久久免费国产精品特黄| 成人综合久久精品色婷婷|