• <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>

            每天早晨叫醒你的不是鬧鐘,而是夢(mèng)想

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

            常用鏈接

            留言簿(1)

            我參與的團(tuán)隊(duì)

            搜索

            •  

            最新評(píng)論

            閱讀排行榜

            評(píng)論排行榜

            1、 在介紹靜態(tài)對(duì)象、全局對(duì)象與程序的運(yùn)行機(jī)制之間的關(guān)系之前,我們首先看一下atexit函數(shù)。

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

            參數(shù)為函數(shù)指針,返回值為整型,0表示成功,其他表示失敗。當(dāng)程序運(yùn)行結(jié)束時(shí),他調(diào)用atexit函數(shù)注冊(cè)的所有函數(shù)。如果多次調(diào)用atexit函數(shù),那么系統(tǒng)將以LIFO(last-in-first-out)的方式調(diào)用所有的注冊(cè)函數(shù)。

            舉例如下(代碼摘自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



            編譯、運(yùn)行程序后,程序的輸出為:
            This is executed first.
            This is executed next.
            注冊(cè)函數(shù)的順序?yàn)椋篺n1、fn2、fn3、fn4,但是調(diào)用順序?yàn)閒n4、fn3、fn2、fn1。

            2、理解了atexit函數(shù)之后,我們就可以來看看局部靜態(tài)對(duì)象了。

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


            在調(diào)試狀態(tài)下,匯編代碼如下(請(qǐng)觀察藍(lán)色標(biāo)記出來的代碼):
            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]為方便說明加入的字符,實(shí)際代碼中并不存在。
            [1]語句很明顯的調(diào)用AAA的構(gòu)造函數(shù)。
            [2]語句將442410h壓入棧中。
            [3]語句調(diào)用atexit函數(shù),根據(jù)我們的了解,atexit的參數(shù)應(yīng)該是函數(shù)指針。那么我們來分析一下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調(diào)用規(guī)范的風(fēng)格。
            [2]語句調(diào)用AAA的析構(gòu)函數(shù)。

            程序結(jié)束時(shí),將調(diào)用atexit函數(shù)注冊(cè)的442410h處的函數(shù),進(jìn)而調(diào)用了AAA的析構(gòu)函數(shù)。從而保證了析構(gòu)函數(shù)的調(diào)用。

             
            3、   了解了局部靜態(tài)對(duì)象之后,我們來看看全局對(duì)象。
            我們知道全局對(duì)象必須在main函數(shù)前已經(jīng)被構(gòu)造。為了弄清楚全局對(duì)象何時(shí)被構(gòu)造,我在全局對(duì)象的實(shí)例化處設(shè)置了斷點(diǎn),調(diào)用堆棧如下:
            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


            作為對(duì)比,我在AAA的析構(gòu)函數(shù)出設(shè)置了斷點(diǎn),調(diào)用堆棧如下:
                 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


            由此我們可以看出程序的實(shí)際入口點(diǎn)位mainCRTStartup而不是main函數(shù)(相對(duì)于ANSI的控制臺(tái)程序而言)。
            我們來分析一下_cinit函數(shù):
            注釋中有一句[3. General C initializer routines],看來該函數(shù)的功能之一是完成C的初始化例程。
            函數(shù)的核心代碼如下:
            /*
                     * do initializations
                     */
                    initret = _initterm_e( __xi_a, __xi_z );
            /*
                     * do C++ initializations
                     */
                    _initterm( __xc_a, __xc_z );
            看來該函數(shù)主要進(jìn)行C、C++的初始化。我們進(jìn)一步分析函數(shù)_initterm_e和_initterm,兩個(gè)函數(shù)的功能進(jìn)本相同,都是遍歷函數(shù)指針(由參數(shù)指定函數(shù)指針的開始位置[__xi_a、__xi_z]、結(jié)束位置[__xc_a、__xc_z]),如果函數(shù)指針不為null,那么調(diào)用該函數(shù)。
            那么__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")
            可以看出這四個(gè)變量分別在數(shù)據(jù)段.CRT$XIA、.CRT$XIZ、.CRT$XCA、.CRT$XCZ中。當(dāng)連接器布局代碼時(shí),它按根據(jù)的名稱,按照字母排序的規(guī)則,排列所有段。這樣在段.CRT$XIA中的變量出現(xiàn)在段.CRT$XIZ所有變量之前,從而形成鏈表。對(duì)于.CRT$XCA、.CRT$XCZ數(shù)據(jù)段同理。最后這四個(gè)數(shù)據(jù)段被合并到.data數(shù)據(jù)段中。
            再看看這些變量的類型,typedef void (__cdecl *_PVFV)(void); 所以這些變量組成了2個(gè)初始化函數(shù)指針鏈表。
            調(diào)試過程中,看到__xc_a、__xc_z鏈表中,指向的初始化函數(shù)很多是構(gòu)造函數(shù),如:
            static std::_Init_locks initlocks;
            static filebuf fout(_cpp_stdout);
            extern _CRTDATA2 ostream cout(&fout);
            cout對(duì)象也在此時(shí)被構(gòu)造。
            對(duì)于析構(gòu)函數(shù)的調(diào)用也是采用相同的方式,只是此時(shí)每一種初始化,都有一種終止函數(shù)與之對(duì)應(yīng)。

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


            本文來自CSDN博客,轉(zhuǎn)載請(qǐng)標(biāo)明出處:http://blog.csdn.net/coffeemay/archive/2006/11/19/1395250.aspx

            posted on 2011-04-29 11:06 沛沛 閱讀(882) 評(píng)論(0)  編輯 收藏 引用 所屬分類: C++
            国产精品久久久香蕉| 亚洲va中文字幕无码久久不卡| 国产精品久久久久久久久免费| 久久免费精品一区二区| 亚洲午夜福利精品久久| 热久久国产精品| 久久久精品波多野结衣| 久久亚洲国产午夜精品理论片| 国内精品久久久久久久久电影网| www.久久热.com| 亚洲伊人久久综合中文成人网| 婷婷久久久亚洲欧洲日产国码AV | 精品久久久久久无码中文野结衣| 午夜精品久久久久久影视777| 欧美777精品久久久久网| 噜噜噜色噜噜噜久久| 一级做a爰片久久毛片免费陪| 亚洲AV无码一区东京热久久| 久久91精品综合国产首页| 久久婷婷激情综合色综合俺也去| 成人综合久久精品色婷婷| 情人伊人久久综合亚洲| 久久久久99精品成人片欧美| 亚洲国产精品无码久久SM| 国产成人香蕉久久久久| 老司机国内精品久久久久| 伊人久久大香线蕉亚洲| 伊人久久无码中文字幕| 国产精品美女久久久久AV福利| 久久夜色精品国产欧美乱| 久久人妻无码中文字幕| 青青草原精品99久久精品66| 一级A毛片免费观看久久精品| 伊人久久综在合线亚洲2019| 色偷偷91久久综合噜噜噜噜| 狠狠色丁香婷婷综合久久来来去| 国产精品一区二区久久不卡| 精品熟女少妇aⅴ免费久久| 国产精品免费福利久久| 国产精品9999久久久久| 久久精品国产精品亚洲毛片 |