青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品

Young's Blog

包子鋪

用匯編對(duì)C++代碼的優(yōu)化前后進(jìn)行分析

下面通過對(duì)C++程序匯編代碼的分析,加深編譯器對(duì)C++程序優(yōu)化的理解

1. C++程序如下:
#include<cstdio>
using std::printf;

class MyParent
{
public:
    MyParent()
    {} 
    ~MyParent()
    {} 
    void DoJob()
    {  
        Test();
    }  
private:
    virtual void Test()
    {  
        printf("Parent::Test\n");
    }  
};

class MyChild:public MyParent
{
public:
    MyChild()
    {} 
    ~MyChild()
    {} 
private:
    virtual void Test()
    {  
        printf("Child::Test\n");
    }  
};

int main()
{
    MyChild obj;
    MyParent *p=&obj;
    p->DoJob();
}


2.要注意的概念和寄存器表示的約定
    2.1 SFP(Stack Frame Pointer)
            在Intel的CPU中ESP永遠(yuǎn)指向棧頂,EBP永遠(yuǎn)指向棧底,同時(shí)棧由高地址向低地址增長(zhǎng)。
        通常在調(diào)用一個(gè)函數(shù)的時(shí)候,下一條指令地址被入棧,方便在函數(shù)結(jié)束時(shí)通過ret返回到下一條
        指令繼續(xù)執(zhí)行。在查看調(diào)用函數(shù)的代碼時(shí),經(jīng)常會(huì)看到下面的指令
   
            pushl %ebp                
            mov %esp, %ebp           
//建立一個(gè)SFP
            ... ...
            leave                     //相當(dāng)于mov %ebp, %esp和popl %ebp兩條指令
            ret

        這個(gè)SFP用來(lái)確定當(dāng)前函數(shù)棧的邊界,而且可以通過EBP得到上一層函數(shù)棧的棧底。一般通過這種
        方式來(lái)取傳入的參數(shù)
            mov 0x8(%ebp), xxx
        因?yàn)榻FP時(shí)pushl的EBP占用4個(gè)字節(jié),而之前壓入棧的EIP又占用4個(gè)字節(jié)。

    2.2 C++中的vtable和vptr
            C++的每個(gè)對(duì)象都會(huì)有一個(gè)vptr指針,用來(lái)指向vtable的地址,而vtable是一個(gè)函數(shù)指
        針數(shù)組,分別指向各個(gè)類定義的虛函數(shù)。在GCC編譯的代碼中,vptr在對(duì)象的低地址。

            *vptr         -->得到vtable
            (*vptr)[1]    -->得到vtable[1]
          
    2.3 顏色約定   
        這種顏色表示ESP,這種顏色表示EBP,這種顏色表示應(yīng)該要注意的數(shù)據(jù)。

3.未進(jìn)行-O2 優(yōu)化時(shí)的匯編代碼
代碼如下:
Dump of assembler code for function main:
0x080485a0 <main+0>:     lea    0x4(%esp),%ecx
0x080485a4 <main+4>:     and    $0xfffffff0,%esp
0x080485a7 <main+7>:     pushl  0xfffffffc(%ecx)
0x080485aa <main+10>:    push   %ebp
0x080485ab <main+11>:    mov    %esp,%ebp
0x080485ad <main+13>:    push   %ebx
0x080485ae <main+14>:    push   %ecx
0x080485af <main+15>:    sub    $0x30,%esp
0x080485b2 <main+18>:    lea    0xfffffff0(%ebp),%eax
0x080485b5 <main+21>:    mov    %eax,(%esp)
0x080485b8 <main+24>:    call   0x8048650 <_ZN7MyChildC1Ev>
0x080485bd <main+29>:    lea    0xfffffff0(%ebp),%eax
0x080485c0 <main+32>:    mov    %eax,0xfffffff4(%ebp)
0x080485c3 <main+35>:    mov    0xfffffff4(%ebp),%eax
0x080485c6 <main+38>:    mov    %eax,(%esp)
0x080485c9 <main+41>:    call   0x8048630 <_ZN8MyParent5DoJobEv>
0x080485ce <main+46>:    lea    0xfffffff0(%ebp),%eax
0x080485d1 <main+49>:    mov    %eax,(%esp)
0x080485d4 <main+52>:    call   0x8048670 <_ZN7MyChildD1Ev>
0x080485d9 <main+57>:    mov    $0x0,%eax
0x080485de <main+62>:    mov    %eax,0xffffffe4(%ebp)
0x080485e1 <main+65>:    jmp    0x8048602 <main+98>
0x080485e3 <main+67>:    mov    %eax,0xffffffe0(%ebp)
0x080485e6 <main+70>:    mov    0xffffffe0(%ebp),%ebx
0x080485e9 <main+73>:    lea    0xfffffff0(%ebp),%eax
0x080485ec <main+76>:    mov    %eax,(%esp)
0x080485ef <main+79>:    call   0x8048670 <_ZN7MyChildD1Ev>
0x080485f4 <main+84>:    mov    %ebx,0xffffffe0(%ebp)
0x080485f7 <main+87>:    mov    0xffffffe0(%ebp),%eax
0x080485fa <main+90>:    mov    %eax,(%esp)
0x080485fd <main+93>:    call   0x8048480 <_init+100>
0x08048602 <main+98>:    mov    0xffffffe4(%ebp),%eax
0x08048605 <main+101>:   add    $0x30,%esp
0x08048608 <main+104>:   pop    %ecx
0x08048609 <main+105>:   pop    %ebx
0x0804860a <main+106>:   pop    %ebp
0x0804860b <main+107>:   lea    0xfffffffc(%ecx),%esp
0x0804860e <main+110>:   ret   
0x0804860f <main+111>:   nop   
End of assembler dump.

從這里開始分析整個(gè)代碼:
Dump of assembler code for function main:
0x080485a0 <main+0>:     lea    0x4(%esp),%ecx
0x080485a4 <main+4>:     and    $0xfffffff0,%esp
0x080485a7 <main+7>:     pushl  0xfffffffc(%ecx)
0x080485aa <main+10>:    push   %ebp
0x080485ab <main+11>:    mov    %esp,%ebp
--------------------------------------------------------------------------
上面這段代碼做了進(jìn)入main函數(shù)時(shí)的準(zhǔn)備工作,同時(shí)創(chuàng)建了SPF,此時(shí)的堆棧與寄存器如下:

0xbfbfe830:    0xbfbfe9a0    0x2828cdc0    0xbfbfe858    0x00000001
0xbfbfe840:    0xbfbfe89c    0x00000000    0xbfbfe868    0x2824a6b9
0xbfbfe850:    0x00000018    0x00000001    0xbfbfe88c    0x08048529
6: /x $eax = 0xbfbfe894
5: /x $ebx = 0x1
4: /x $ecx = 0xbfbfe870
   
3: /x $edx = 0x0
2: /x $ebp = 0xbfbfe858
1: /x $esp = 0xbfbfe858

========================== 下面繼續(xù)代碼======================================

0x080485ad <main+13>:    push   %ebx
0x080485ae <main+14>:    push   %ecx
0x080485af <main+15>:    sub    $0x30,%esp
0x080485b2 <main+18>:    lea    0xfffffff0(%ebp),%eax

0x080485b5 <main+21>:    mov    %eax,(%esp)
0x080485b8 <main+24>:    call   0x8048650 <_ZN7MyChildC1Ev>
--------------------------------------------------------------------------
    這兩段代碼分別完成:
    1.首先保存EBX和ECX的數(shù)據(jù),然后準(zhǔn)備了48(0x30)個(gè)字節(jié)的空間,同時(shí)保存MyChild的this指
      針的地址到EAX。
    2.把this指針入棧,調(diào)用MyChild::MyChild()進(jìn)行構(gòu)造。

調(diào)用MyChild::MyChild()之前的堆棧與寄存器內(nèi)容如下:

0xbfbfe800:    0x28070814    0xbfbfe844    0x2804d998    0x28074e24
0xbfbfe810:    0x00000001    0xbfbfe834    0x00000001    0x00000000
0xbfbfe820:    0xbfbfe848[1] 0x00000001    0xbfbfe980    0xbfbfe988
0xbfbfe830:    0xbfbfe9a0    0x2828cdc0    0xbfbfe858    0x00000001
0xbfbfe840:    0xbfbfe89c    0x00000000    0xbfbfe868[2] 0x2824a6b9
[3]
0xbfbfe850:    0xbfbfe870    0x00000001    0xbfbfe88c    0x08048529
6: /x $eax = 0xbfbfe848
5: /x $ebx = 0x1
4: /x $ecx = 0xbfbfe870
3: /x $edx = 0x0
2: /x $ebp = 0xbfbfe858
1: /x $esp = 0xbfbfe820

    這里[1]保存著this指針(也就是[2]的地址),注意[2]后面的[3],就是MyParent的指針空間
(MyParent *p),現(xiàn)在保存的地址為0x2824a6b9還沒有進(jìn)行賦值。
    在C++中一個(gè)空類的sizeof大小為1,這個(gè)值是在編譯時(shí)期就計(jì)算出來(lái)的,而在實(shí)際情況中,如果
一個(gè)空類沒有虛函數(shù),那么在內(nèi)存中占1個(gè)字節(jié),否則就占用4個(gè)字節(jié)(vptr用)。
    此時(shí)[2]這塊空間就是vptr指針的空間,[2]的地址則是this指針?biāo)赶虻牡刂罚@個(gè)時(shí)候vptr還
沒有初始化。


下面要開始調(diào)用MyChild::MyChild(),代碼如下:
    Dump of assembler code for function _ZN7MyChildC1Ev:
    0x08048650 <_ZN7MyChildC1Ev+0>:     push   %ebp
    0x08048651 <_ZN7MyChildC1Ev+1>:     mov    %esp,%ebp
    0x08048653 <_ZN7MyChildC1Ev+3>:     sub    $0x8,%esp
    0x08048656 <_ZN7MyChildC1Ev+6>:     mov    0x8(%ebp),%eax
    0x08048659 <_ZN7MyChildC1Ev+9>:     mov    %eax,(%esp)
    0x0804865c <_ZN7MyChildC1Ev+12>:    call   0x8048610 <_ZN8MyParentC2Ev>
    0x08048661 <_ZN7MyChildC1Ev+17>:    mov    $0x8048780,%edx
    0x08048666 <_ZN7MyChildC1Ev+22>:    mov    0x8(%ebp),%eax
    0x08048669 <_ZN7MyChildC1Ev+25>:    mov    %edx,(%eax)
    0x0804866b <_ZN7MyChildC1Ev+27>:    leave 
    0x0804866c <_ZN7MyChildC1Ev+28>:    ret   
    0x0804866d <_ZN7MyChildC1Ev+29>:    nop   
    0x0804866e <_ZN7MyChildC1Ev+30>:    nop   
    0x0804866f <_ZN7MyChildC1Ev+31>:    nop   
    End of assembler dump.
   
        我們從這里開始分析MyChild::MyChild()的代碼:
       
Dump of assembler code for function _ZN7MyChildC1Ev:
        0x08048650 <_ZN7MyChildC1Ev+0>:     push   %ebp
        0x08048651 <_ZN7MyChildC1Ev+1>:     mov    %esp,%ebp
        0x08048653 <_ZN7MyChildC1Ev+3>:     sub    $0x8,%esp
        0x08048656 <_ZN7MyChildC1Ev+6>:     mov    0x8(%ebp),%eax
        
--------------------------------------------------------------------------
            這里建立SFP后,最后把剛才傳入的this指針保存到EAX中。從堆棧的內(nèi)容可以知道,EBP+8后剛好
        是
[2],地址為 0xbfbfe820,這里保存就是入?yún)ⅲ籟1]的位置保存著EIP。

        0xbfbfe800:    0x28070814    0xbfbfe844    0x2804d998    0x28074e24
        0xbfbfe810:    0x00000001    0xbfbfe834    0xbfbfe858    0x080485bd[1]
        0xbfbfe820:    0xbfbfe848[2] 0x00000001    0xbfbfe980    0xbfbfe988
        0xbfbfe830:    0xbfbfe9a0    0x2828cdc0    0xbfbfe858    0x00000001
        0xbfbfe840:    0xbfbfe89c    0x00000000    0xbfbfe868    0x2824a6b9
        0xbfbfe850:    0xbfbfe870    0x00000001    0xbfbfe88c    0x08048529
        6: /x $eax = 0xbfbfe848
        5: /x $ebx = 0x1
        4: /x $ecx = 0xbfbfe870
        3: /x $edx = 0x0
        2: /x $ebp = 0xbfbfe818
        1: /x $esp = 0xbfbfe810

        
========================== 下面繼續(xù)代碼======================================

        0x08048659 <_ZN7MyChildC1Ev+9>:     mov    %eax,(%esp)
        0x0804865c <_ZN7MyChildC1Ev+12>:    call   0x8048610 <_ZN8MyParentC2Ev>
       
--------------------------------------------------------------------------
            再次把this指針入棧,然后調(diào)用MyChild的基類MyParent::MyParent構(gòu)造函數(shù),代碼如下:
        Dump of assembler code for function _ZN8MyParentC2Ev:
        0x08048610 <_ZN8MyParentC2Ev+0>:     push   %ebp
        0x08048611 <_ZN8MyParentC2Ev+1>:     mov    %esp,%ebp
        0x08048613 <_ZN8MyParentC2Ev+3>:     mov    $0x80487b8,%edx
        0x08048618 <_ZN8MyParentC2Ev+8>:     mov    0x8(%ebp),%eax
        0x0804861b <_ZN8MyParentC2Ev+11>:    mov    %edx,(%eax)
        0x0804861d <_ZN8MyParentC2Ev+13>:    pop    %ebp
        0x0804861e <_ZN8MyParentC2Ev+14>:    ret   
        0x0804861f <_ZN8MyParentC2Ev+15>:    nop   
        End of assembler dump.
       
        我們從這里開始分析MyParent::MyParent的代碼:
    
        0x08048610 <_ZN8MyParentC2Ev+0>:     push   %ebp
            0x08048611 <_ZN8MyParentC2Ev+1>:     mov    %esp,%ebp
            0x08048613 <_ZN8MyParentC2Ev+3>:     mov    $0x80487b8,%edx

           
--------------------------------------------------------------------------
                同樣地建立SFP.但是
0x80487b8是什么?這個(gè)是MyParent的vtable地址,而EDX我們就是所謂
            的vptr指針了。我們可以驗(yàn)證一下:

            (gdb) x 0x80487b8
            0x80487b8 <_ZTV8MyParent+8>:    0x080486b0

                由于vtable是一個(gè)函數(shù)指針數(shù)組,所以我們用"x
0x80487b8"得到的0x080486b0其實(shí)是vtable[0],
            接下來(lái):

            (gdb) x 0x080486b0
            0x80486b0 <_ZN8MyParent4TestEv>:    0x83e58955
        
                這個(gè)才是MyParent::Test的真實(shí)地址。用C++filt還原上面的"
_ZTV8MyParent+8"和
            "
_ZN8MyParent4TestEv":

           
_ZTV8MyParent+8           vtable for MyParent+8
            _ZN8MyParent4TestEv       MyParent::Test()

           
==========================下面 繼續(xù)代碼======================================

            0x08048618 <_ZN8MyParentC2Ev+8>:     mov    0x8(%ebp),%eax
            0x0804861b <_ZN8MyParentC2Ev+11>:    mov    %edx,(%eax)
            --------------------------------------------------------------------------
                得到MyParent的vtable地址后,我們需要設(shè)置MyChild的vptr指向MyParent的vtable。這里
            首先取出來(lái)的就是MyChild對(duì)象的this指針,前面也說(shuō)過了,vptr指針在對(duì)象的頭4個(gè)字節(jié),所以這時(shí)候
            EAX保存的也就是MyChild的vptr的地址。
                然后把MyParent的vtable地址賦值給MyChild的vptr,堆棧和寄存器內(nèi)容如下:

            0xbfbfe800:    0x28070814    0xbfbfe844    0xbfbfe818    0x08048661
            0xbfbfe810:    0xbfbfe848[1] 0xbfbfe834    0xbfbfe858    0x080485bd
            0xbfbfe820:    0xbfbfe848    0x00000001    0xbfbfe980    0xbfbfe988
            0xbfbfe830:    0xbfbfe9a0    0x2828cdc0    0xbfbfe858    0x00000001
            0xbfbfe840:    0xbfbfe89c    0x00000000    0x080487b8[2] 0x2824a6b9
            0xbfbfe850:    0xbfbfe870    0x00000001    0xbfbfe88c    0x08048529
            6: /x $eax = 0xbfbfe848
            5: /x $ebx = 0x1
            4: /x $ecx = 0xbfbfe870
            3: /x $edx = 0x80487b8
            2: /x $ebp = 0xbfbfe808
            1: /x $esp = 0xbfbfe808

               由于這里ESP和EBP是一樣的地址,[1]實(shí)際上是0x8(%ebp)的位置,[2]是MyChild對(duì)象的vptr指針
            的空間,現(xiàn)在已經(jīng)指向MyParent的vtable地址了

           
==========================下面 繼續(xù)代碼======================================
            0x0804861d <_ZN8MyParentC2Ev+13>:    pop    %ebp
            0x0804861e <_ZN8MyParentC2Ev+14>:    ret   
            0x0804861f <_ZN8MyParentC2Ev+15>:    nop 
           
--------------------------------------------------------------------------
            由于沒有增加堆棧空間,EBP和ESP是一樣的,所以不需要LEAVE指令,直接POP出EBP。

        從這里結(jié)束對(duì)MyParent::MyParent()的分析,下面繼續(xù)MyChild::MyChild()的分析
        
==========================下面 繼續(xù)代碼======================================

        0x08048661 <_ZN7MyChildC1Ev+17>:    mov    $0x8048780,%edx
        0x08048666 <_ZN7MyChildC1Ev+22>:    mov    0x8(%ebp),%eax
        0x08048669 <_ZN7MyChildC1Ev+25>:    mov    %edx,(%eax)
        0x0804866b <_ZN7MyChildC1Ev+27>:    leave 
        0x0804866c <_ZN7MyChildC1Ev+28>:    ret   
        0x0804866d <_ZN7MyChildC1Ev+29>:    nop   
        0x0804866e <_ZN7MyChildC1Ev+30>:    nop   
        0x0804866f <_ZN7MyChildC1Ev+31>:    nop   
        --------------------------------------------------------------------------
            從MyParent::MyParent()返回以后,現(xiàn)在執(zhí)行MyChild::MyChild()。這里首先也是把vtable
        地址保存到EDX,取出MyChild對(duì)象的this指針,然后設(shè)置vptr指向MyChild的vtable.由于整個(gè)構(gòu)造函數(shù)
        都是空的,所以這里設(shè)置完vptr后就直接返回,下面是設(shè)置完vptr后的寄存器和堆棧內(nèi)容:

        0xbfbfe800:    0x28070814    0xbfbfe844    0xbfbfe818    0x08048661
        0xbfbfe810:    0xbfbfe848    0xbfbfe834    0xbfbfe858    0x080485bd
        0xbfbfe820:    0xbfbfe848[1] 0x00000001    0xbfbfe980    0xbfbfe988
        0xbfbfe830:    0xbfbfe9a0    0x2828cdc0    0xbfbfe858    0x00000001
        0xbfbfe840:    0xbfbfe89c    0x00000000    0x08048780[2] 0x2824a6b9
        0xbfbfe850:    0xbfbfe870    0x00000001    0xbfbfe88c    0x08048529
        6: /x $eax = 0xbfbfe848
        5: /x $ebx = 0x1
        4: /x $ecx = 0xbfbfe870
        3: /x $edx = 0x8048780
        2: /x $ebp = 0xbfbfe818
        1: /x $esp = 0xbfbfe810

            這里[1]是0x8(%ebp)的位置,[2]是(%eax)的位置。可以看到已經(jīng)把EDX的值賦給[2]了。

        這里結(jié)束對(duì)MyChild::MyChild()代碼的分析。

        
==========================下面繼 續(xù)代碼======================================
    
    0x080485bd <main+29>:    lea    0xfffffff0(%ebp),%eax
    0x080485c0 <main+32>:    mov    %eax,0xfffffff4(%ebp)
    0x080485c3 <main+35>:    mov    0xfffffff4(%ebp),%eax
    0x080485c6 <main+38>:    mov    %eax,(%esp)
    0x080485c9 <main+41>:    call   0x8048630 <_ZN8MyParent5DoJobEv>
   
--------------------------------------------------------------------------
        構(gòu)造函數(shù)執(zhí)行完畢后,我們把MyChild對(duì)象的指針給了MyParent指針,然后調(diào)用了DoJob方法。
    在調(diào)用DoJob之前的寄存器和堆棧地址為:

    0xbfbfe800:    0x28070814    0xbfbfe844    0xbfbfe818    0x08048661
    0xbfbfe810:    0xbfbfe848    0xbfbfe834    0xbfbfe858    0x080485bd
    0xbfbfe820:    0xbfbfe848[3] 0x00000001    0xbfbfe980    0xbfbfe988
    0xbfbfe830:    0xbfbfe9a0    0x2828cdc0    0xbfbfe858    0x00000001
    0xbfbfe840:    0xbfbfe89c    0x00000000    0x08048780[1] 0xbfbfe848[2]
    0xbfbfe850:    0xbfbfe870    0x00000001    0xbfbfe88c    0x08048529
    6: /x $eax = 0xbfbfe848
    5: /x $ebx = 0x1
    4: /x $ecx = 0xbfbfe870
    3: /x $edx = 0x8048780
    2: /x $ebp = 0xbfbfe858
    1: /x $esp = 0xbfbfe820

        首先保存[1]的地址
(this指針)到EAX,再保存到[2],這里是"MyParent *p"的指針空間。這樣子就完
    成了語(yǔ)句"MyParent *p=&obj"的賦值,然后再把"MyParent *p"所保存的地址入棧為調(diào)用MyParent::
    DoJob做準(zhǔn)備。

    MyParent::DoJob的代碼如下:
    Dump of assembler code for function _ZN8MyParent5DoJobEv:
    0x08048630 <_ZN8MyParent5DoJobEv+0>:     push   %ebp
    0x08048631 <_ZN8MyParent5DoJobEv+1>:     mov    %esp,%ebp
    0x08048633 <_ZN8MyParent5DoJobEv+3>:     sub    $0x8,%esp
    0x08048636 <_ZN8MyParent5DoJobEv+6>:     mov    0x8(%ebp),%eax
    0x08048639 <_ZN8MyParent5DoJobEv+9>:     mov    (%eax),%eax
    0x0804863b <_ZN8MyParent5DoJobEv+11>:    mov    (%eax),%edx
    0x0804863d <_ZN8MyParent5DoJobEv+13>:    mov    0x8(%ebp),%eax
    0x08048640 <_ZN8MyParent5DoJobEv+16>:    mov    %eax,(%esp)
    0x08048643 <_ZN8MyParent5DoJobEv+19>:    call   *%edx
    0x08048645 <_ZN8MyParent5DoJobEv+21>:    leave 
    0x08048646 <_ZN8MyParent5DoJobEv+22>:    ret   
    0x08048647 <_ZN8MyParent5DoJobEv+23>:    nop   
    0x08048648 <_ZN8MyParent5DoJobEv+24>:    nop   
    0x08048649 <_ZN8MyParent5DoJobEv+25>:    nop   
    0x0804864a <_ZN8MyParent5DoJobEv+26>:    nop   
    0x0804864b <_ZN8MyParent5DoJobEv+27>:    nop   
    0x0804864c <_ZN8MyParent5DoJobEv+28>:    nop   
    0x0804864d <_ZN8MyParent5DoJobEv+29>:    nop   
    0x0804864e <_ZN8MyParent5DoJobEv+30>:    nop   
    0x0804864f <_ZN8MyParent5DoJobEv+31>:    nop   
    End of assembler dump.
    
    從這里開始MyParent::DoJob的代碼分析:
       
0x08048630 <_ZN8MyParent5DoJobEv+0>:     push   %ebp
        0x08048631 <_ZN8MyParent5DoJobEv+1>:     mov    %esp,%ebp
        0x08048633 <_ZN8MyParent5DoJobEv+3>:     sub    $0x8,%esp
        0x08048636 <_ZN8MyParent5DoJobEv+6>:     mov    0x8(%ebp),%eax

        0x08048639 <_ZN8MyParent5DoJobEv+9>:     mov    (%eax),%eax
        0x0804863b <_ZN8MyParent5DoJobEv+11>:    mov    (%eax),%edx

        0x0804863d <_ZN8MyParent5DoJobEv+13>:    mov    0x8(%ebp),%eax
        0x08048640 <_ZN8MyParent5DoJobEv+16>:    mov    %eax,(%esp)
        0x08048643 <_ZN8MyParent5DoJobEv+19>:    call   *%edx
       
--------------------------------------------------------------------------
            上面的代碼分成三部分:
        1.
這個(gè)時(shí)候已經(jīng)把堆棧中的 this指針取出來(lái)保存到EAX中。
        2.取出MyChild的vtable地址保存到EAX
,再取出vtable[0]保存到EDX
        3.取出堆棧中的this指針保存到EAX,同時(shí)入棧,然后調(diào)用vtable[0]的函數(shù)
    
            在調(diào)用vtable[0]函數(shù)之前的寄存器和堆棧數(shù)據(jù)如下:
        
        0xbfbfe800:    0x28070814    0xbfbfe844    0xbfbfe818    0x08048661    
        0xbfbfe810:    0xbfbfe848    0xbfbfe834    0xbfbfe858    0x080485ce
        0xbfbfe820:    0xbfbfe848[1] 0x00000001    0xbfbfe980    0xbfbfe988
        0xbfbfe830:    0xbfbfe9a0    0x2828cdc0    0xbfbfe858    0x00000001
        0xbfbfe840:    0xbfbfe89c    0x00000000    0x08048780[2] 0xbfbfe848
        0xbfbfe850:    0xbfbfe870    0x00000001    0xbfbfe88c    0x08048529
        6: /x $eax = 0xbfbfe848
        5: /x $ebx = 0x1
        4: /x $ecx = 0xbfbfe870
        3: /x $edx = 0x8048690
        2: /x $ebp = 0xbfbfe818
        1: /x $esp = 0xbfbfe810
        
            先從[1]的地方取出this指針保存到EAX中,然后再通過
"mov    (%eax),%eax",取出[2](0x08048780)
        保存到EAX,最后再通過"
mov    (%eax),%edx"取出vtable保存到EDX中,我們驗(yàn)證一下:
        
        (gdb) x 0x08048780
        0x8048780 <_ZTV7MyChild+8>:    0x08048690
        此時(shí)EDX保存的就是
0x08048690,最后 我們調(diào)用"call   *%edx"的時(shí)候,就等于調(diào)用vtable[0]:

        (gdb) x 0x08048690
        0x8048690 <_ZN7MyChild4TestEv>:    0x83e58955
        
        用c++filt對(duì)上面的字符串進(jìn)行還原:
        
_ZTV7MyChild+8          vtable for MyChild+8
        
_ZN7MyChild4TestEv      MyChild::Test()
        
        現(xiàn)在下一步就直接進(jìn)入MyChild::Test執(zhí)行,代碼如下:
        Dump of assembler code for function _ZN7MyChild4TestEv:
        0x08048690 <_ZN7MyChild4TestEv+0>:     push   %ebp
        0x08048691 <_ZN7MyChild4TestEv+1>:     mov    %esp,%ebp
        0x08048693 <_ZN7MyChild4TestEv+3>:     sub    $0x8,%esp
        0x08048696 <_ZN7MyChild4TestEv+6>:     movl   $0x804875d,(%esp)
        0x0804869d <_ZN7MyChild4TestEv+13>:    call   0x8048440 <_init+36>
        0x080486a2 <_ZN7MyChild4TestEv+18>:    leave 
        0x080486a3 <_ZN7MyChild4TestEv+19>:    ret   
        0x080486a4 <_ZN7MyChild4TestEv+20>:    nop   
        0x080486a5 <_ZN7MyChild4TestEv+21>:    nop   
        0x080486a6 <_ZN7MyChild4TestEv+22>:    nop   
        0x080486a7 <_ZN7MyChild4TestEv+23>:    nop   
        0x080486a8 <_ZN7MyChild4TestEv+24>:    nop   
        0x080486a9 <_ZN7MyChild4TestEv+25>:    nop   
        0x080486aa <_ZN7MyChild4TestEv+26>:    nop   
        0x080486ab <_ZN7MyChild4TestEv+27>:    nop   
        0x080486ac <_ZN7MyChild4TestEv+28>:    nop   
        0x080486ad <_ZN7MyChild4TestEv+29>:    nop   
        0x080486ae <_ZN7MyChild4TestEv+30>:    nop   
        0x080486af <_ZN7MyChild4TestEv+31>:    nop   
        End of assembler dump.

        
從這里我們分析MyChild::Test()的代碼:
            0x08048690 <_ZN7MyChild4TestEv+0>:     push   %ebp
            0x08048691 <_ZN7MyChild4TestEv+1>:     mov    %esp,%ebp
            0x08048693 <_ZN7MyChild4TestEv+3>:     sub    $0x8,%esp
            0x08048696 <_ZN7MyChild4TestEv+6>:     movl   $0x804875d,(%esp)
            0x0804869d <_ZN7MyChild4TestEv+13>:    call   0x8048440 <_init+36>
            0x080486a2 <_ZN7MyChild4TestEv+18>:    leave 
            0x080486a3 <_ZN7MyChild4TestEv+19>:    ret
            
--------------------------------------------------------------------------
               由于MyChild::Test()只是簡(jiǎn)單地打印字符串,所以這里并沒有用到堆棧中的this指針,只是把字符
            串常量的地址
0x804875d入棧,然后打印。在調(diào)用call之前的寄存器和堆棧如下:

            0xbfbfe7f0:    0x28070814    0xbfbfe804    0x2804fd26    0x28078040
            0xbfbfe800:    0x0804875d    0xbfbfe844    0xbfbfe818    0x08048645
            0xbfbfe810:    0xbfbfe848    0xbfbfe834    0xbfbfe858    0x080485ce
            0xbfbfe820:    0xbfbfe848    0x00000001    0xbfbfe980    0xbfbfe988
            0xbfbfe830:    0xbfbfe9a0    0x2828cdc0    0xbfbfe858    0x00000001
            0xbfbfe840:    0xbfbfe89c    0x00000000    0x08048780    0xbfbfe848
            0xbfbfe850:    0xbfbfe870    0x00000001    0xbfbfe88c    0x08048529
            6: /x $eax = 0xbfbfe848
            5: /x $ebx = 0x1
            4: /x $ecx = 0xbfbfe870
            3: /x $edx = 0x8048690
            2: /x $ebp = 0xbfbfe808
            1: /x $esp = 0xbfbfe800
 
            我們來(lái)看一下
0x804875d的內(nèi)容:
            (gdb) x /s 0x0804875d
            0x804875d <_fini+97>:     "Child::Test"
           
            而
這里調(diào)用的0x8048440是標(biāo)準(zhǔn)庫(kù)中的puts函 數(shù)。到這里為止,接下來(lái)的MyChild::Test的代碼就是
            返回和nop指令了,我們直接從MyChild::Test返回到MyParent::DoJob的執(zhí)行中

        從這里結(jié)束MyChild::Test()的代碼分析
        --------------------------------------------------------------------------
        由于MyParent::DoJob()剩下的也是返回和nop指令,這里直接返回到main中

    從這里結(jié)束MyParent::DoJob的代碼分析
   
--------------------------------------------------------------------------
    從MyParent::DoJob返回后,寄存器和堆棧的內(nèi)容如下:

    0xbfbfe800:    0x0804875d    0xbfbfe844    0xbfbfe818    0x08048645
    0xbfbfe810:    0xbfbfe848    0xbfbfe834    0xbfbfe858    0x080485ce
    0xbfbfe820:    0xbfbfe848    0x00000001    0xbfbfe980    0xbfbfe988
    0xbfbfe830:    0xbfbfe9a0    0x2828cdc0    0xbfbfe858    0x00000001
    0xbfbfe840:    0xbfbfe89c    0x00000000    0x08048780    0xbfbfe848
    0xbfbfe850:    0xbfbfe870    0x00000001    0xbfbfe88c    0x08048529
    6: /x $eax = 0xa
    5: /x $ebx = 0x1
    4: /x $ecx = 0xc
    3: /x $edx = 0x0
    2: /x $ebp = 0xbfbfe858
    1: /x $esp = 0xbfbfe820

    
========================== 下面繼續(xù)代碼======================================
    
    0x080485ce <main+46>:    lea    0xfffffff0(%ebp),%eax
    0x080485d1 <main+49>:    mov    %eax,(%esp)
    0x080485d4 <main+52>:    call   0x8048670 <_ZN7MyChildD1Ev>
    
   
--------------------------------------------------------------------------
        執(zhí)行完DoJob后,現(xiàn)在整個(gè)main函數(shù)要結(jié)束,接下來(lái)首先調(diào)用的就是MyChild::~MyChild()。
    這里傳入this指針到EAX,然后入棧調(diào)用MyChild的析構(gòu)函數(shù)。在調(diào)用析構(gòu)函數(shù)之前的寄存器和堆棧內(nèi)容如下:

    0xbfbfe800:    0x0804875d    0xbfbfe844    0xbfbfe818    0x08048645
    0xbfbfe810:    0xbfbfe848    0xbfbfe834    0xbfbfe858    0x080485ce
    0xbfbfe820:    0xbfbfe848[1] 0x00000001    0xbfbfe980    0xbfbfe988
    0xbfbfe830:    0xbfbfe9a0    0x2828cdc0    0xbfbfe858    0x00000001
    0xbfbfe840:    0xbfbfe89c    0x00000000    0x08048780    0xbfbfe848
    0xbfbfe850:    0xbfbfe870    0x00000001    0xbfbfe88c    0x08048529
    6: /x $eax = 0xbfbfe848
    5: /x $ebx = 0x1
    4: /x $ecx = 0xc
    3: /x $edx = 0x0
    2: /x $ebp = 0xbfbfe858
    1: /x $esp = 0xbfbfe820

        上面的[1]為入棧的this指針,MyChild::~MyChild()的代碼如下:

    Dump of assembler code for function _ZN7MyChildD1Ev:
    0x08048670 <_ZN7MyChildD1Ev+0>:     push   %ebp
    0x08048671 <_ZN7MyChildD1Ev+1>:     mov    %esp,%ebp
    0x08048673 <_ZN7MyChildD1Ev+3>:     sub    $0x8,%esp
    0x08048676 <_ZN7MyChildD1Ev+6>:     mov    $0x8048780,%eax
    0x0804867b <_ZN7MyChildD1Ev+11>:    mov    0x8(%ebp),%edx
    0x0804867e <_ZN7MyChildD1Ev+14>:    mov    %eax,(%edx)
    0x08048680 <_ZN7MyChildD1Ev+16>:    mov    0x8(%ebp),%eax
    0x08048683 <_ZN7MyChildD1Ev+19>:    mov    %eax,(%esp)
    0x08048686 <_ZN7MyChildD1Ev+22>:    call   0x8048620 <_ZN8MyParentD2Ev>
    0x0804868b <_ZN7MyChildD1Ev+27>:    leave 
    0x0804868c <_ZN7MyChildD1Ev+28>:    ret   
    0x0804868d <_ZN7MyChildD1Ev+29>:    nop   
    0x0804868e <_ZN7MyChildD1Ev+30>:    nop   
    0x0804868f <_ZN7MyChildD1Ev+31>:    nop   
    End of assembler dump.

    從這里開始分析MyChild::~MyChild()的代碼:
       
0x08048670 <_ZN7MyChildD1Ev+0>:     push   %ebp
        0x08048671 <_ZN7MyChildD1Ev+1>:     mov    %esp,%ebp
        0x08048673 <_ZN7MyChildD1Ev+3>:     sub    $0x8,%esp

        0x08048676 <_ZN7MyChildD1Ev+6>:     mov    $0x8048780,%eax
        0x0804867b <_ZN7MyChildD1Ev+11>:    mov    0x8(%ebp),%edx
        0x0804867e <_ZN7MyChildD1Ev+14>:    mov    %eax,(%edx)

        0x08048680 <_ZN7MyChildD1Ev+16>:    mov    0x8(%ebp),%eax
        0x08048683 <_ZN7MyChildD1Ev+19>:    mov    %eax,(%esp)
        0x08048686 <_ZN7MyChildD1Ev+22>:    call   0x8048620 <_ZN8MyParentD2Ev>

        --------------------------------------------------------------------------
            上面的代碼分成三部分
        1.建立SFP,保留8字節(jié)堆棧空間
        2.設(shè)置MyChild的vtable到EAX,保存this指針到EDX,然后設(shè)置this指針的vptr指向MyChild的vtable
        3.this指針入棧,調(diào)用
MyParent::~MyParent()函數(shù)

        在調(diào)用
MyParent::~MyParent()之前的寄存器和堆棧內(nèi)容如下:

        0xbfbfe800:    0x0804875d    0xbfbfe844    0xbfbfe818    0x08048645
        0xbfbfe810:    0xbfbfe848    0xbfbfe834    0xbfbfe858    0x080485d9
        0xbfbfe820:    0xbfbfe848    0x00000001    0xbfbfe980    0xbfbfe988
        0xbfbfe830:    0xbfbfe9a0    0x2828cdc0    0xbfbfe858    0x00000001
        0xbfbfe840:    0xbfbfe89c    0x00000000    0x08048780[1] 0xbfbfe848
        0xbfbfe850:    0xbfbfe870    0x00000001    0xbfbfe88c    0x08048529
        6: /x $eax = 0xbfbfe848
        5: /x $ebx = 0x1
        4: /x $ecx = 0xc
        3: /x $edx = 0xbfbfe848
        2: /x $ebp = 0xbfbfe818
        1: /x $esp = 0xbfbfe810

        上面[1]為MyChild的vtable地址,驗(yàn)證如下:
        (gdb) x 0x08048780
        0x8048780 <_ZTV7MyChild+8>:    0x08048690

        (gdb) x 0x08048690
        0x8048690 <_ZN7MyChild4TestEv>:    0x83e58955

        經(jīng)過c++filt的還原:
       
_ZTV7MyChild+8         vtable for MyChild+8
       
_ZN7MyChild4TestEv     MyChild::Test()

        MyParent::~MyParent()的代碼如下:
        Dump of assembler code for function _ZN8MyParentD2Ev:
        0x08048620 <_ZN8MyParentD2Ev+0>:     push   %ebp
        0x08048621 <_ZN8MyParentD2Ev+1>:     mov    %esp,%ebp
        0x08048623 <_ZN8MyParentD2Ev+3>:     mov    $0x80487b8,%edx
        0x08048628 <_ZN8MyParentD2Ev+8>:     mov    0x8(%ebp),%eax
        0x0804862b <_ZN8MyParentD2Ev+11>:    mov    %edx,(%eax)
        0x0804862d <_ZN8MyParentD2Ev+13>:    pop    %ebp
        0x0804862e <_ZN8MyParentD2Ev+14>:    ret   
        0x0804862f <_ZN8MyParentD2Ev+15>:    nop   
        End of assembler dump.
        除了把vtable地址保存到EDX之后,再賦值給this指針的vptr,其他的什么也沒有做。
        下面接著從MyChild::~MyChild()返回之后

    
==========================下面繼 續(xù)代碼======================================

    
0x080485d9 <main+57>:    mov    $0x0,%eax
    0x080485de <main+62>:    mov    %eax,0xffffffe4(%ebp)
    0x080485e1 <main+65>:    jmp    0x8048602 <main+98>

    0x080485e3 <main+67>:    mov    %eax,0xffffffe0(%ebp)
    0x080485e6 <main+70>:    mov    0xffffffe0(%ebp),%ebx
    0x080485e9 <main+73>:    lea    0xfffffff0(%ebp),%eax
    0x080485ec <main+76>:    mov    %eax,(%esp)
    0x080485ef <main+79>:    call   0x8048670 <_ZN7MyChildD1Ev>
    0x080485f4 <main+84>:    mov    %ebx,0xffffffe0(%ebp)
    0x080485f7 <main+87>:    mov    0xffffffe0(%ebp),%eax
    0x080485fa <main+90>:    mov    %eax,(%esp)
    0x080485fd <main+93>:    call   0x8048480 <_init+100>

    0x08048602 <main+98>:    mov    0xffffffe4(%ebp),%eax
    0x08048605 <main+101>:   add    $0x30,%esp
    0x08048608 <main+104>:   pop    %ecx
    0x08048609 <main+105>:   pop    %ebx
    0x0804860a <main+106>:   pop    %ebp
    0x0804860b <main+107>:   lea    0xfffffffc(%ecx),%esp
    0x0804860e <main+110>:   ret   
    0x0804860f <main+111>:   nop   
    End of assembler dump.
    
--------------------------------------------------------------------------
    剩下的三部分代碼主要就是做出棧,異常處理和后繼的工作
    1.上面執(zhí)行到"
jmp    0x8048602 <main+98>"后就跳到第3部分
    2.這塊調(diào)用_Unwind_Resume,屬于C++的異常處理部分
    3.出棧處理,從main函數(shù)返回


4.進(jìn)行-O2優(yōu)化后的代碼
代碼如下:
Dump of assembler code for function main:
0x080485a0 <main+0>:     lea    0x4(%esp),%ecx
0x080485a4 <main+4>:     and    $0xfffffff0,%esp
0x080485a7 <main+7>:     pushl  0xfffffffc(%ecx)
0x080485aa <main+10>:    push   %ebp
0x080485ab <main+11>:    mov    %esp,%ebp
0x080485ad <main+13>:    push   %ecx
0x080485ae <main+14>:    sub    $0x24,%esp
0x080485b1 <main+17>:    lea    0xfffffff8(%ebp),%eax
0x080485b4 <main+20>:    movl   $0x80486b0,0xfffffff8(%ebp)
0x080485bb <main+27>:    mov    %eax,(%esp)
0x080485be <main+30>:    call   *0x80486b0
0x080485c4 <main+36>:    add    $0x24,%esp
0x080485c7 <main+39>:    xor    %eax,%eax
0x080485c9 <main+41>:    pop    %ecx
0x080485ca <main+42>:    pop    %ebp
0x080485cb <main+43>:    lea    0xfffffffc(%ecx),%esp
0x080485ce <main+46>:    ret   
0x080485cf <main+47>:    mov    %eax,(%esp)
0x080485d2 <main+50>:    call   0x8048480 <_init+100>
0x080485d7 <main+55>:    nop   
0x080485d8 <main+56>:    nop   
0x080485d9 <main+57>:    nop   
0x080485da <main+58>:    nop   
0x080485db <main+59>:    nop   
0x080485dc <main+60>:    nop   
0x080485dd <main+61>:    nop   
0x080485de <main+62>:    nop   
0x080485df <main+63>:    nop   
End of assembler dump.

下面開始分析
代碼:

    0x080485a0 <main+0>:     lea    0x4(%esp),%ecx
    0x080485a4 <main+4>:     and    $0xfffffff0,%esp
    0x080485a7 <main+7>:     pushl  0xfffffffc(%ecx)
    0x080485aa <main+10>:    push   %ebp
    0x080485ab <main+11>:    mov    %esp,%ebp
    0x080485ad <main+13>:    push   %ecx
    0x080485ae <main+14>:    sub    $0x24,%esp

    --------------------------------------------------------------------------
        程序開始,建立SFP,ECX入棧并保存36個(gè)字節(jié)空間后的寄存器和堆棧內(nèi)容如下:

    0xbfbfe810:    0x00000001    0xbfbfe834    0x00000001    0x00000000
    0xbfbfe820:    0x00000000    0x00000001    0xbfbfe984    0xbfbfe98c
    0xbfbfe830:    0xbfbfe9a4    0x2828cdc0    0xbfbfe858    0x00000001
    0xbfbfe840:    0xbfbfe8a0    0x00000000    0xbfbfe868    0x2824a6b9
    0xbfbfe850:    0x00000020    0xbfbfe870    0xbfbfe890    0x08048569
    7: /x $eax = 0xbfbfe898
    6: /x $ebx = 0x1
    5: /x $ecx = 0xbfbfe870
    3: /x $edx = 0x0
    2: /x $ebp = 0xbfbfe858
    1: /x $esp = 0xbfbfe830


    ==========================下面繼續(xù)代碼======================================

    0x080485b1 <main+17>:    lea    0xfffffff8(%ebp),%eax
    0x080485b4 <main+20>:    movl   $0x80486b0,0xfffffff8(%ebp)
   
--------------------------------------------------------------------------
        這段代碼把this指針保存到EAX中,再把MyChild的vtable直接賦值給[1](這里就是this指針)
    的vptr空間。

        寄存器和堆棧內(nèi)容如下:

    0xbfbfe810:    0x00000001    0xbfbfe834    0x00000001    0x00000000
    0xbfbfe820:    0x00000000    0x00000001    0xbfbfe980    0xbfbfe988
    0xbfbfe830:    0xbfbfe9a0    0x2828cdc0    0xbfbfe858    0x00000001
    0xbfbfe840:    0xbfbfe89c    0x00000000    0xbfbfe868    0x2824a6b9
    0xbfbfe850:    0x080486b0[1] 0xbfbfe870    0xbfbfe88c    0x08048529
    6: /x $eax = 0xbfbfe850
    5: /x $ebx = 0x1
    4: /x $ecx = 0xbfbfe870
    3: /x $edx = 0x0
    2: /x $ebp = 0xbfbfe858
    1: /x $esp = 0xbfbfe830

    ==========================下面繼續(xù)代碼======================================  
    0x080485bb <main+27>:    mov    %eax,(%esp)
    0x080485be <main+30>:    call   *0x80486b0
   
--------------------------------------------------------------------------
        這里this指針入棧后,并不通過vptr,直接調(diào)用vtable[0]的函數(shù),此時(shí)vptr形同虛設(shè),直接被
    編譯器無(wú)視。驗(yàn)證一下
*0x80486b0 的處理函數(shù):

        (gdb) x 0x80486b0
        0x80486b0 <_ZTV7MyChild+8>:    0x080485e0
        (gdb) x 0x080485e0
        0x80485e0 <_ZN7MyChild4TestEv>:    0xc7e58955

    進(jìn)行還原后
       
_ZTV7MyChild+8          vtable for MyChild+8
       
_ZN7MyChild4TestEv      MyChild::Test()
    
    MyChild::Test()的代碼如下:
    Dump of assembler code for function _ZN7MyChild4TestEv:
    0x080485e0 <_ZN7MyChild4TestEv+0>:     push   %ebp
    0x080485e1 <_ZN7MyChild4TestEv+1>:     mov    %esp,%ebp
    0x080485e3 <_ZN7MyChild4TestEv+3>:     movl   $0x804868c,0x8(%ebp)
    0x080485ea <_ZN7MyChild4TestEv+10>:    pop    %ebp
    0x080485eb <_ZN7MyChild4TestEv+11>:    jmp    0x8048440 <_init+36>
    End of assembler dump.

    從這里開始MyChild::Test()的代碼分析:
       
0x080485e0 <_ZN7MyChild4TestEv+0>:     push   %ebp
        0x080485e1 <_ZN7MyChild4TestEv+1>:     mov    %esp,%ebp
        0x080485e3 <_ZN7MyChild4TestEv+3>:     movl   $0x804868c,0x8(%ebp)
       
--------------------------------------------------------------------------
            這里的"
movl   $0x804868c,0x8(%ebp)"直接破壞掉剛才入棧傳入的this指針,0x804868c
        地址保存的就是我們要打印的字符串:

        (gdb) x /s 0x0804868c
        0x804868c <_fini+96>:     "Child::Test"

       
==========================下面 繼續(xù)代碼======================================
       
        0x080485ea <_ZN7MyChild4TestEv+10>:    pop    %ebp
        0x080485eb <_ZN7MyChild4TestEv+11>:    jmp    0x8048440 <_init+36>
       
--------------------------------------------------------------------------
            保存字符串地址在堆棧中后,現(xiàn)在直接構(gòu)造puts函數(shù)調(diào)用之前的堆棧,由于每次在進(jìn)入函數(shù)時(shí)都會(huì)執(zhí)行
        "push %ebp"和"mov %esp, %ebp"而且每次讀取入?yún)⒍际峭ㄟ^0x8(%ebp)來(lái)讀取,所以這里必需要先
        pop掉EBP,一但jmp進(jìn)入puts函數(shù),執(zhí)行"push %ebp"和"mov %esp, %ebp"后,字符串地址存放的位
        置剛好就是0x8(%ebp)。

        pop %ebp之前的堆棧([1]為字符串地址):

        0xbfbfe810:    0x00000001    0xbfbfe834    0x00000001    0x00000000
        0xbfbfe820:    0x00000000    0x00000001    0xbfbfe858    0x080485c4
        0xbfbfe830:    0x0804868c[1] 0x2828cdc0    0xbfbfe858    0x00000001
        0xbfbfe840:    0xbfbfe89c    0x00000000    0xbfbfe868    0x2824a6b9
        0xbfbfe850:    0x080486b0    0xbfbfe870    0xbfbfe88c    0x08048529
        6: /x $eax = 0xbfbfe850
        5: /x $ebx = 0x1
        4: /x $ecx = 0xbfbfe870
        3: /x $edx = 0x0
        2: /x $ebp = 0xbfbfe828
        1: /x $esp = 0xbfbfe828


        pop %ebp之后的堆棧,在jmp進(jìn)入之前:

        0xbfbfe810:    0x00000001    0xbfbfe834    0x00000001    0x00000000
        0xbfbfe820:    0x00000000    0x00000001    0xbfbfe858    0x080485c4
        0xbfbfe830:    0x0804868c[1] 0x2828cdc0    0xbfbfe858    0x00000001
        0xbfbfe840:    0xbfbfe89c    0x00000000    0xbfbfe868    0x2824a6b9
        0xbfbfe850:    0x080486b0    0xbfbfe870    0xbfbfe88c    0x08048529
        6: /x $eax = 0xbfbfe850
        5: /x $ebx = 0x1
        4: /x $ecx = 0xbfbfe870
        3: /x $edx = 0x0
        2: /x $ebp = 0xbfbfe858
        1: /x $esp = 0xbfbfe82c

        在jmp進(jìn)入puts函數(shù)后,打印出結(jié)果后程序就結(jié)束了。main函數(shù)中的下面的這部分代碼沒有執(zhí)行過
        
========================== 下面繼續(xù)代碼======================================
        0x080485c4 <main+36>:    add    $0x24,%esp
        0x080485c7 <main+39>:    xor    %eax,%eax
        0x080485c9 <main+41>:    pop    %ecx
        0x080485ca <main+42>:    pop    %ebp
        0x080485cb <main+43>:    lea    0xfffffffc(%ecx),%esp
        0x080485ce <main+46>:    ret   
        0x080485cf <main+47>:    mov    %eax,(%esp)
        0x080485d2 <main+50>:    call   0x8048480 <_init+100>
        0x080485d7 <main+55>:    nop   
        0x080485d8 <main+56>:    nop   
        0x080485d9 <main+57>:    nop   
        0x080485da <main+58>:    nop   
        0x080485db <main+59>:    nop   
        0x080485dc <main+60>:    nop   
        0x080485dd <main+61>:    nop   
        0x080485de <main+62>:    nop   
        0x080485df <main+63>:    nop   

        --------------------------------------------------------------------------  
分析到此結(jié)束。

總結(jié):
    用C++開發(fā)盡量使用優(yōu)化選項(xiàng),虛擬函數(shù)的調(diào)用除了要多幾次給vptr賦值不同的vtable地址以外,效率和普通
函數(shù)是差不多的。類繼承深度越長(zhǎng)導(dǎo)致的性能問題在于要執(zhí)行的基類的構(gòu)造函數(shù),需要初始化的數(shù)據(jù)成員越多,繼承鏈
越長(zhǎng),調(diào)用的構(gòu)造函數(shù)越多(inline后可以顯著提高這部分的效率)。

4.與優(yōu)化后C程序的對(duì)比
程序如下:
#include <stdio.h>

void Test();

int main()
{
    Test();
}

void Test()
{
    printf("Child::Test\n");
}

編譯使用-O2優(yōu)化,得到的匯編代碼:

main函數(shù):
Dump of assembler code for function main:
0x08048420 <main+0>:     lea    0x4(%esp),%ecx
0x08048424 <main+4>:     and    $0xfffffff0,%esp
0x08048427 <main+7>:     pushl  0xfffffffc(%ecx)
0x0804842a <main+10>:    push   %ebp
0x0804842b <main+11>:    mov    %esp,%ebp
0x0804842d <main+13>:    push   %ecx
0x0804842e <main+14>:    sub    $0x4,%esp
0x08048431 <main+17>:    call   0x8048400 <Test>
0x08048436 <main+22>:    add    $0x4,%esp
0x08048439 <main+25>:    pop    %ecx
0x0804843a <main+26>:    pop    %ebp
0x0804843b <main+27>:    lea    0xfffffffc(%ecx),%esp
0x0804843e <main+30>:    ret   
0x0804843f <main+31>:    nop   
End of assembler dump.

Test函數(shù):
Dump of assembler code for function Test:
0x08048400 <Test+0>:     push   %ebp
0x08048401 <Test+1>:     mov    %esp,%ebp
0x08048403 <Test+3>:     sub    $0x8,%esp
0x08048406 <Test+6>:     movl   $0x80484cc,(%esp)
0x0804840d <Test+13>:    call   0x80482b8 <_init+36>
0x08048412 <Test+18>:    leave 
0x08048413 <Test+19>:    ret   
0x08048414 <Test+20>:    lea    0x0(%esi),%esi
0x0804841a <Test+26>:    lea    0x0(%edi),%edi
End of assembler dump.

單單從匯編代碼上面來(lái)看,C++的比C的多(異常處理的部分等等),C++使用jmp到打印函數(shù)的方法,而
C依然使用call的方式調(diào)用,在puts函數(shù)內(nèi)部的處理,C++也應(yīng)該做了一些特殊的處理(jmp進(jìn)入,打印
完后直接退出程序,沒有執(zhí)行main函數(shù)中的出棧動(dòng)作)。相比較于C而言,C++的優(yōu)化是
另辟蹊徑了。

以上結(jié)果使用
GCC 4.2.1
GAS 2.15
GDB 6.1.1    







posted on 2010-03-07 23:25 Young 閱讀(766) 評(píng)論(0)  編輯 收藏 引用


只有注冊(cè)用戶登錄后才能發(fā)表評(píng)論。
網(wǎng)站導(dǎo)航: 博客園   IT新聞   BlogJava   博問   Chat2DB   管理


青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品
  • <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>
            国产一区二区丝袜高跟鞋图片| 久久最新视频| 久久夜色精品| 欧美极品在线观看| 久久爱91午夜羞羞| 欧美精品福利视频| 日韩视频在线永久播放| 国产一区91精品张津瑜| 久久久久久久久综合| 亚洲欧美在线另类| 国产精品五月天| 亚洲欧美视频在线观看视频| 亚洲欧美日韩精品在线| 国产亚洲精品久久久久久| 久久久xxx| 亚洲高清资源综合久久精品| 1000部精品久久久久久久久| 久久精品理论片| 亚洲日本成人在线观看| 午夜精品在线看| 久久久久久久一区二区三区| 国产亚洲成av人在线观看导航| 久久九九全国免费精品观看| 欧美国产日本韩| 亚洲欧美日韩在线不卡| 国产一区二区在线观看免费| 欧美电影在线观看完整版| 午夜精品国产精品大乳美女| 亚洲第一精品久久忘忧草社区| 亚洲视频一区二区免费在线观看| 国产精品va在线播放| 久久精品99无色码中文字幕| 亚洲品质自拍| 久久婷婷麻豆| 欧美亚洲专区| 亚洲尤物影院| 亚洲国产另类久久精品| 国产精品一区二区在线| 欧美国产亚洲另类动漫| 久久精品国产亚洲5555| 亚洲欧美日本国产有色| 日韩视频一区二区| 欧美激情一区二区三区在线视频观看 | 国产女优一区| 蜜桃av一区二区| 久久精品国产亚洲5555| 亚洲男人第一网站| 中文av字幕一区| 亚洲黄色影院| 亚洲伦理中文字幕| 日韩亚洲欧美中文三级| 91久久精品网| 女生裸体视频一区二区三区| 欧美日韩午夜激情| 欧美中文日韩| 蜜桃av噜噜一区| 欧美精品久久久久久久| 欧美日韩国产一区二区| 欧美日韩精品免费| 欧美视频在线免费看| 国产欧美精品| 国内精品视频在线观看| 伊人影院久久| 99国产精品久久久久老师| 亚洲一区二区四区| 久久精品久久99精品久久| 欧美成人午夜激情在线| 亚洲国产一区二区a毛片| 亚洲最新在线视频| 久久国产精品久久久久久电车| 久久麻豆一区二区| 国产精品vip| 亚洲国产成人在线视频| 一区二区免费在线视频| 欧美一区免费视频| 最近看过的日韩成人| 亚洲欧美日韩国产综合| 欧美韩日视频| 国产一区二区精品久久91| 亚洲精品一区二区三区在线观看 | 在线视频日韩| 午夜宅男久久久| 亚洲精品一区久久久久久 | 亚洲精品国久久99热| 亚洲一级二级在线| 欧美视频免费在线观看| 亚洲高清一区二| 久久成人综合网| 一区二区欧美视频| 欧美日韩日日骚| 99re6热在线精品视频播放速度| 久久综合九色综合欧美就去吻| 亚洲网友自拍| 亚洲国产精品第一区二区三区| 91久久久在线| 欧美日产在线观看| 中日韩午夜理伦电影免费| 91久久国产自产拍夜夜嗨| 欧美激情偷拍| 日韩西西人体444www| 亚洲精品资源| 欧美精选在线| 亚洲欧美伊人| 亚洲欧美精品伊人久久| 国产婷婷一区二区| 久久久久成人精品| 久久青草欧美一区二区三区| 在线免费不卡视频| 欧美国产精品专区| 欧美高清视频| 欧美一区影院| 久久综合狠狠| 黄色精品网站| 亚洲乱码国产乱码精品精| 国产精品久久久久久久午夜| 亚洲欧美日韩精品久久久| 欧美一区91| 最新中文字幕亚洲| 亚洲一区二区三区精品动漫| 亚洲国产天堂久久综合| 99热这里只有精品8| 狠狠色综合网| 亚洲在线播放电影| 日韩网站免费观看| 久久精品噜噜噜成人av农村| 亚洲免费在线播放| 欧美jjzz| 欧美不卡激情三级在线观看| 欧美视频一区二区三区在线观看 | 欧美成人一区二区三区片免费| 欧美国产精品va在线观看| 午夜精品美女自拍福到在线 | 亚洲综合日韩| 久久久精品国产免费观看同学| 亚洲欧美一区二区三区久久 | 一区二区免费在线播放| 欧美日韩国产一级片| 久久久午夜精品| 国产亚洲a∨片在线观看| 亚洲淫性视频| 欧美影视一区| 激情视频一区| 欧美在线|欧美| 美女日韩在线中文字幕| 国产精品系列在线播放| 亚洲视频欧洲视频| 久久精品国产v日韩v亚洲| 欧美jizzhd精品欧美巨大免费| 亚洲欧美在线一区| 亚洲欧美日韩另类精品一区二区三区 | 欧美日韩在线播放三区| 激情av一区| 免费成人毛片| 欧美精品综合| 一本色道精品久久一区二区三区| 久久综合色婷婷| 久久久久高清| 亚洲国产裸拍裸体视频在线观看乱了中文 | 欧美午夜不卡影院在线观看完整版免费 | 久久高清国产| 国产日韩欧美在线播放不卡| 久久国产加勒比精品无码| 欧美刺激午夜性久久久久久久| 一本色道久久综合狠狠躁篇的优点 | 久久伊人亚洲| 欧美激情视频在线免费观看 欧美视频免费一 | 99视频精品全部免费在线| 欧美精品v日韩精品v韩国精品v | 亚洲精品日本| 亚洲欧洲av一区二区| 在线成人av网站| 国产精品高精视频免费| 欧美在线一二三区| 亚洲激情一区二区| 久久久久一区二区三区四区| 亚洲第一精品影视| 国产亚洲欧美另类一区二区三区| 久久综合激情| 亚洲欧美日韩高清| 亚洲第一福利在线观看| 久久精品国产一区二区三区| 亚洲一二三四久久| 一本一本a久久| 亚洲国产欧美国产综合一区| 国产精品v欧美精品∨日韩| 玖玖精品视频| 亚洲欧美电影院| 亚洲精品国产精品国自产观看浪潮| 亚洲自拍偷拍色片视频| 91久久综合| 在线观看不卡av| 国产精品视屏| 国产精品日韩在线播放| 欧美视频中文字幕在线| 欧美精品福利视频| 欧美成人午夜77777| 久久婷婷国产综合精品青草| 美国十次了思思久久精品导航| 久久野战av| 欧美激情aⅴ一区二区三区|