從彙編看c++的new和delete

下面是c++源碼:c++

class X {
private:
    int _x;
public:
    X(int xx = 0) : _x(xx) {}
    ~X() {}
};


int main() {
    X* xp = new X;
    delete xp;
  
}

代碼很簡單,在main函數裏面先用new構造一個堆對象,而後用delelte釋放此對象。數組

接下來看構造堆對象的彙編碼:函數

  :     X* xp = new X;

    push    4;壓入對象的大小4byte,爲調用operator new函數傳遞參數
    call    ??2@YAPAXI@Z                ; 調用operator new函數
    add    esp, 4;operator new調用結束,堆棧指針下移4byte,釋放爲operator new的參數分配的棧空間
    mov    DWORD PTR $T2579[ebp], eax;寄存器eax裏面存放申請到的堆空間首地址,存入臨時變量ST2579
    cmp    DWORD PTR $T2579[ebp], 0;將臨時變量ST2579的值與0比較,即檢測申請的對空間首地址是否爲NULL
    je    SHORT $LN3@main;若是檢測結果相等,就跳轉到標號$LN3@main處執行,不然順序執行。這裏是順序執行
    push    0;將0壓棧,爲調用構造函數傳遞參數
    mov    ecx, DWORD PTR $T2579[ebp];將臨時變量ST2579的值(裏面保存申請到的堆空間首地址)給寄存器ecx,做爲隱含參數傳遞給構造函數
                                  ;這個隱含參數就是this指針
    call    ??0X@@QAE@H@Z                ; 調用對象的構造函數
    mov    DWORD PTR tv70[ebp], eax;構造函數調用完畢,寄存器eax裏面存放的是對象首地址,這裏將首地址給臨時變量tv70
    jmp    SHORT $LN4@main;跳轉到標號$LN4@main處執行
$LN3@main:
    mov    DWORD PTR tv70[ebp], 0;若是申請對空間失敗,將0給臨時變量tv70
$LN4@main:
    mov    eax, DWORD PTR tv70[ebp];將tv70的值給寄存器eax
    mov    DWORD PTR _xp$[ebp], eax;將寄存器eax的值給指針變量xp

從彙編碼能夠看到,用new構造堆對象時,大體的流程是:this

1 調用operator new申請堆空間編碼

2 對申請到的堆空間首地址進行檢查,防止申請失敗,返回空指針 spa

3 若是申請堆空間成功,就調用構造函數 並將堆空間首地址給xp指針。scala

所以,c++中用new申請堆空間與用malloc不一樣,前者自動檢測堆空間是否申請成功。代理

下面看析構堆對象的彙編碼:指針

 :     delete xp;

    mov    ecx, DWORD PTR _xp$[ebp];將xp指針的值(即堆對象首地址)給寄存器ecx
    mov    DWORD PTR $T2591[ebp], ecx;將ecx的值給臨時變量ST2591
    mov    edx, DWORD PTR $T2591[ebp];將臨時變量ST2591的值給寄存器edx
    mov    DWORD PTR $T2590[ebp], edx;將寄存器edx裏面的值給臨時變量ST2590
    cmp    DWORD PTR $T2590[ebp], 0;將臨時變量ST2590的值與0比較,即檢測傳進來的堆對象首地址是否爲空指針
    je    SHORT $LN5@main;若是爲空指針,則跳轉到標號$LN5@main處執行,不然,順序執行。這裏順序執行
    push    1;壓入對象類型標誌 1 單個對象 3 對象數組  0 只調用析構函數,不調用釋放堆空間(在多重繼承時有用)
mov ecx, DWORD PTR $T2590[ebp];將臨時變量ST2590的值給寄存器ecx,做爲隱函數參數(this指針)傳遞給析構代理函數 call ??_GX@@QAEPAXI@Z;調用析構代理函數 mov DWORD PTR tv75[ebp], eax;析構代理函數執行完畢,寄存器eax裏面保存堆對象首地址,將eax的值給臨時變量tv75 jmp SHORT $LN1@main;跳轉到標號$LN1@main處執行 $LN5@main: mov DWORD PTR tv75[ebp], 0;若是檢測堆對象首地址失敗,臨時變量tv75的值賦0 $LN1@main:;標號後面是main函數結束時的代碼

從彙編碼能夠看到,調用delete並不像operator new同樣直接調用析構函數和operator delete函數,而是調用了析構代理函數來完成operator delete的功能。之因此要用代理函數,是由於某些狀況下要釋放的對象不止一個。code

下面是析構代理函數彙編碼:

 

??_GX@@QAEPAXI@Z PROC                    ; X::`scalar deleting destructor', COMDAT
; _this$ = ecx
    push    ebp
    mov    ebp, esp
    push    ecx;寄存器ecx裏面存儲的是對對象首地址,這裏壓棧的目的是爲了保存這個地址預留空間
    mov    DWORD PTR _this$[ebp], ecx;將ecx裏面的值存到剛纔預留的空間裏面
    mov    ecx, DWORD PTR _this$[ebp];將堆對象首地址給寄存器ecx,做爲隱含參數傳遞給析構函數
    call    ??1X@@QAE@XZ                ; 調用析構函數
    mov    eax, DWORD PTR ___flags$[ebp];這裏獲取在調用析構代理函數以前,傳進來的標誌,並將其給寄存器eax
    and    eax, 1;將eax裏面(對象類型標誌)的值與1相與 目的是判斷是否調用delete函數釋放堆空間
je SHORT $LN1@scalar;若是相與的結果爲0,則跳到標號$LN1@scalar處執行,不然,順序執行,這裏順序執行 mov ecx, DWORD PTR _this$[ebp];將堆對象首地址做爲給寄存器ecx push ecx;壓棧ecx的值,爲調用delete函數傳遞參數 call ??3@YAXPAX@Z ; 調用delete函數 add esp, 4;delete函數調用完畢,棧頂指針下移4byte,釋放爲delete函數的參數分配的棧空間 $LN1@scalar: mov eax, DWORD PTR _this$[ebp];將堆對象首地址給寄存器eax
;作爲返回值
mov esp, ebp pop ebp ret 4 ??_GX@@QAEPAXI@Z ENDP

從彙編碼能夠看到,析構代理函數先調用真正的析構函數,而後再調用delete函數釋放申請到的堆空間。在這中間有一個判斷過程,即經過對象類型標誌,判斷是否調用operator delete釋放堆空間。

經過上面的彙編碼,能夠看到delete的流程大體是:

1 檢測對象首地址值是否爲空

2 若是不爲空,就調用析構代理函數,不然,就不調用析構代理函數

3 在析構代理函數裏面調用對象的析構函數和delete函數(若是對象標誌不爲0的話)

從對c++中的delete調用過程來看,delete不會自動的將指針變量xp的值清零。所以,後續程序中若是使用xp指針仍然可行。指針變量xp和xp所指向的對象,最大的差異就是哪個聲明已經結束了。調用delete後,xp所指向的對象已將被析構,變得不合法,可是地址自己仍然表明一個合法的程序空間。

對象數組

下面來看new和delete應用於對象數組的狀況

c++源碼以下:

class X {
private:
    int _x;
public:
    X(int xx = 0) : _x(xx) {}
    ~X() {}
};


int main() {
    X* xp = new X[2];
    delete [] xp;
  
}

上述代碼在用new在堆中構造了2個對象,而後經過delete釋放。

下面裏看構造過程彙編碼:

 

   X* xp = new X[2];
00F035BD  push        0Ch ;爲調用operator new運算符傳遞參數,即要申請的堆空間的大小
                          ;這裏每一個對象只有4byte,可是卻要申請12byte,是由於對於申請數組對象
                          ;堆空間首地址4byte用來存儲對象個數
00F035BF  call        operator new ;調用operator new運算符 
00F035C4  add         esp,4 ;operator new調用結束,棧頂指針下移4byte,釋放爲operator new傳遞參數而分配的棧空間
00F035C7  mov         dword ptr [ebp-0F8h],eax;寄存器eax裏面保存了申請到的堆空間首地址
00F035CD  mov         dword ptr [ebp-4],0  ;
00F035D4  cmp         dword ptr [ebp-0F8h],0  ;將返回的堆空間首地址與0比較,檢測是否申請成功
00F035DB  je          main+97h (0F03617h)  ;若是申請不成功,即返回NULL,則跳到地址0F03617h處執行 不然,順序執行 這裏順序執行
00F035DD  mov         eax,dword ptr [ebp-0F8h]  ;將堆空間首地址給寄存器eax
00F035E3  mov         dword ptr [eax],2  ;將2寫入堆空間首地址所在內存,即在堆空間首地址4byte處保存對象個數
00F035E9  push        offset X::~X (0F011DBh) ;將析構函數地址壓棧,做爲構造代理函數參數 
00F035EE  push        offset X::`default constructor closure' ;將構造函數地址壓棧,做爲構造代理函數參數  
00F035F3  push        2  ;將對象個數壓棧,做爲構造代理函數參數
00F035F5  push        4  ;將對象大小壓棧,做爲構造代理函數參數
00F035F7  mov         ecx,dword ptr [ebp-0F8h] ;將堆空間首地址給寄存器ecx 
00F035FD  add         ecx,4  ;ecx裏面的值加4 即跳過堆空間首地址4byte,此時ecx裏面保存的是第一個堆對象首地址
00F03600  push        ecx  ;壓棧ecx,做爲構造代理函數參數
00F03601  call        `eh vector constructor iterator' (0F011E5h)  ;調用構造代理函數
00F03606  mov         edx,dword ptr [ebp-0F8h]  ;將堆空間首地址給寄存器edx
00F0360C  add         edx,4  ;edx裏面的值加4 即跳過堆空間首地址4byte,此時edx裏面保存的是第一個堆對象首地址
00F0360F  mov         dword ptr [ebp-10Ch],edx  
00F03615  jmp         main+0A1h (0F03621h)  ;跳到地址0F03621h處執行
00F03617  mov         dword ptr [ebp-10Ch],0  ;若是堆空間申請失敗,賦空指針
00F03621  mov         eax,dword ptr [ebp-10Ch]  
00F03627  mov         dword ptr [ebp-104h],eax  
00F0362D  mov         dword ptr [ebp-4],0FFFFFFFFh  
00F03634  mov         ecx,dword ptr [ebp-104h]  
00F0363A  mov         dword ptr [ebp-14h],ecx  ;堆空間中第一個堆對象首地址給了xp

上面的彙編碼流程大體是:

1 調用operator new分配堆空間

2 調用構造代理函數構造堆對象,在調用構造代理函數時,經過壓棧,像其傳遞了5個參數,分別是 a)第一個堆對象首地址 b)堆對象大小

c)堆對象個數 d)構造函數地址 e)析構函數地址

3 傳回第一個堆對象首地址,而不是申請到的堆空間首地址

從上面彙編碼中還能夠看到,申請到的堆空間首地址用來存放的是對象的個數,所以,堆空間總大小並非對象大小 * 對象個數

下面是構造代理函數的彙編碼,只看相關部分:

00F03792  mov         dword ptr [ebp-20h],0  
00F03799  mov         dword ptr [ebp-4],0  
00F037A0  mov         dword ptr [ebp-1Ch],0  ;將該內存的值初始化爲0 至關於for循環中,循環變量初始化爲0
00F037A7  jmp         `eh vector constructor iterator'+52h (0F037B2h)  ;跳轉到地址0F037B2h處執行
00F037A9  mov         eax,dword ptr [ebp-1Ch]  ;循環變量的值給寄存器eax
00F037AC  add         eax,1  ;寄存器eax裏面的值加1,即循環變量加1
00F037AF  mov         dword ptr [ebp-1Ch],eax  ;將循環變量保存到內存中
00F037B2  mov         ecx,dword ptr [ebp-1Ch] ;循環變量值給了寄存器ecx 
00F037B5  cmp         ecx,dword ptr [ebp+10h];將ecx裏面的值和 dword ptr [ebp+10h]內存所表明的值(對象個數2)比較,至關於循環變量的比較 
00F037B8  jge         `eh vector constructor iterator'+6Bh (0F037CBh)  ;若是循環變量的值大於等於2,就跳轉到地址0F037CBh執行,不然順序執行 
00F037BA  mov         ecx,dword ptr [ebp+8]  ;將第一個堆對象對象首地址給寄存器ecx,做爲隱含參數給構造函數
00F037BD  call        dword ptr [ebp+14h] ;調用構造函數 
00F037C0  mov         edx,dword ptr [ebp+8]  ;將第一個堆對象首地址給寄存器edx
00F037C3  add         edx,dword ptr [ebp+0Ch] ;edx裏面的值加上對象大小,即修改指針,指向下一個堆對象首地址 
00F037C6  mov         dword ptr [ebp+8],edx ;堆對象首地址首地址保存到內存中 
00F037C9  jmp         `eh vector constructor iterator'+49h (0F037A9h);調轉到地址0F037A9h處執行
;======================下面還有代碼,可是是完成構造函數以後,所以省略========================

構造代理函數的做用就是循環調用構造函數,依次構造數組中的每一對象

下面是釋放數組對象的彙編碼:

delete [] xp;
00A2363D  mov         eax,dword ptr [ebp-14h];將堆空間中第一個對象首地址給寄存器eax
                                             ;接下來是對eax的值的傳遞過程
00A23640  mov         dword ptr [ebp-0E0h],eax  
00A23646  mov         ecx,dword ptr [ebp-0E0h]  
00A2364C  mov         dword ptr [ebp-0ECh],ecx  ;最後eax的值(即堆空間第一個對象首地址)給了ebp-0ECh所在內存
00A23652  cmp         dword ptr [ebp-0ECh],0  ;檢測該內存裏面的值是否爲空
00A23659  je          main+0F0h (0A23670h) ;若是爲空,跳到地址0A23670h處執行,不然,順序執行 這裏是順序執行 
00A2365B  push        3  ;壓入釋放對象類型標誌,1爲單個對象,3爲釋放對象數組,0僅表示執行析構函數,不釋放堆空間
                         ;壓入的值將做爲參數傳遞給析構代理函數
00A2365D  mov         ecx,dword ptr [ebp-0ECh]  ;將堆空間中第一個對象首地址給寄存器ecx,做爲隱含參數傳遞給析構代理函數
00A23663  call        X::`vector deleting destructor' (0A211EAh)  ;調用vector deleting destructor函數
00A23668 mov dword ptr [ebp-10Ch],eax 00A2366E jmp main+0FAh (0A2367Ah) 00A23670 mov dword ptr [ebp-10Ch],0 13: 14: }

 

vector deleting destructor函數的彙編碼:

X::`vector deleting destructor':
00A21B10  push        ebp  
00A21B11  mov         ebp,esp  
00A21B13  sub         esp,0CCh  
00A21B19  push        ebx  
00A21B1A  push        esi  
00A21B1B  push        edi  
00A21B1C  push        ecx  ;在調用該函數以前,ecx寄存器保存的是堆空間中,第一個對象的首地址
                           ;這裏將首地址壓入棧中保存,由於下面的代碼中將用到寄存器ecx
00A21B1D  lea         edi,[ebp-0CCh]  
00A21B23  mov         ecx,33h  
00A21B28  mov         eax,0CCCCCCCCh  
00A21B2D  rep stos    dword ptr es:[edi]  
;==========================================以上代碼爲函數入口部分====================
00A21B2F  pop         ecx  ;將棧頂裏面的值(保存着堆空間中第一個對象首地址)彈出,存到寄存器ecx,
00A21B30  mov         dword ptr [ebp-8],ecx  ;將對象首地址存放到ebp-8所表明的內存
00A21B33  mov         eax,dword ptr [ebp+8]  ;對象類型標標誌被保存在了寄存器eax中
00A21B36  and         eax,2  ;將eax裏面的值和2相與,檢測是否爲對象數組標誌(由於標誌只能爲0 1 3,若是不爲3 結果確定爲0)
00A21B39  je          X::`vector deleting destructor'+61h (0A21B71h);若是結果爲0,即不是對象數組標誌
                                                                    ;跳轉到地址0A21B71h處執行 不然 順序執行 這裏順序執行  
00A21B3B  push        offset X::~X (0A211DBh);將析構函數的地址壓棧,做爲參數傳遞給析構代理函數  
00A21B40  mov         eax,dword ptr [this] ;this指針指向堆空間中第一個對象首地址,這裏將該值給寄存器eax
00A21B43  mov         ecx,dword ptr [eax-4]  ;將向上偏移堆空間中第一個對象首地址4byte處內存內容(即申請到的堆空間首地址處
                                             ;內存內容,該內存裏面保存着對象個數)給寄存器ecx
00A21B46  push        ecx  ;將ecx壓棧,做爲參數傳遞給析構代理函數
00A21B47  push        4  ;將對象大小壓棧,做爲參數傳遞給析構代理函數
00A21B49  mov         edx,dword ptr [this]  ;將堆空間中第一個對象首地址給寄存器edx
00A21B4C  push        edx ;將edx的值壓棧,做爲參數傳遞給析構代理函數 
00A21B4D  call        `eh vector destructor iterator' (0A211F4h) ;調用析構代理函數 
00A21B52  mov         eax,dword ptr [ebp+8]  ;獲取對象類型標誌,其值在調用該函數以前被壓入棧中
00A21B55  and         eax,1;將寄存器eax的值和1相與,目的是判斷是否要調用delete函數釋放堆空間  
00A21B58  je          X::`vector deleting destructor'+59h (0A21B69h)  ;若是相與結果爲0,就跳轉到地址0A21B69h處執行,不然
                                                                      ;順序執行,這裏順序執行
00A21B5A  mov         eax,dword ptr [this]  ;將堆空間中第一個對象首地址給寄存器eax
00A21B5D  sub         eax,4  ;將eax裏面的值減4,即修正了eax裏面的值,此時,eax裏面保存的是申請到的堆空間首地址
00A21B60  push        eax  ;將eax的值壓棧,做爲隱含參數傳遞給operator delete
00A21B61  call        operator delete (0A21087h) ;調用operator delete,釋放堆空間 
00A21B66  add         esp,4  ;調用operator delete結束,釋放爲其傳參時的棧空間
00A21B69  mov         eax,dword ptr [this];將堆空間中第一個對象首地址給寄存器eax  
00A21B6C  sub         eax,4  ;將eax的值減4,即修正eax裏面的值,此時,eax裏面存儲的是申請到的堆空間首地址
00A21B6F  jmp         X::`vector deleting destructor'+80h (0A21B90h)  ;跳轉到地址0A21B90h處執行
;=====================下面代碼是傳進來的對象標誌不是3時執行的代碼=====================
00A21B71  mov         ecx,dword ptr [this] ;若是對象標誌不是3,將跳轉到這裏執行。將堆空間中對象首地址給寄存器ecx,做爲隱含參數調用析構函數 
00A21B74  call        X::~X (0A211DBh)  ;調用析構函數
00A21B79  mov         eax,dword ptr [ebp+8] ;將對象類型標誌給寄存器eax 
00A21B7C  and         eax,1  ;和上面同樣,判斷是否釋放堆空間
00A21B7F  je          X::`vector deleting destructor'+7Dh (0A21B8Dh)  ;若是對象標誌爲0,就跳轉到地址0A21B8Dh處執行
                                                                      ;不然,順序執行
00A21B81  mov         eax,dword ptr [this]  ;將堆對象首地址給寄存器eax
00A21B84  push        eax  ;壓入eax,做爲operator delete的參數
00A21B85  call        operator delete (0A21087h)  ;調用operator delete
00A21B8A  add         esp,4  ;operator delete調用結束,釋放爲其傳遞參數分配的棧空間
00A21B8D  mov         eax,dword ptr [this] ;將堆對象首地址給寄存器eax,做爲返回值 
00A21B90  pop         edi  
00A21B91  pop         esi  
00A21B92  pop         ebx  
00A21B93  add         esp,0CCh  
00A21B99  cmp         ebp,esp  
00A21B9B  call        @ILT+305(__RTC_CheckEsp) (0A21136h)  
00A21BA0  mov         esp,ebp  
00A21BA2  pop         ebp  
00A21BA3  ret         4  

vector deleting destructor整體流程也是先調用析構代理函數,而後調用operator delete釋放空間(若是對象標誌不爲0的話),而且在調用析構代理函數時也傳進4個參數 a)堆空間中第一個對象首地址 b)對象大小 c)對象個數 d)虛函數地址

下面是析構代理函數的彙編碼:

mov         ecx,dword ptr [ebp+0Ch]  ;獲取堆對象個數,給寄存器ecx
00A236E0  imul        ecx,dword ptr [ebp+10h]  ;ebp+10h所表明的內存裏面存放對象大小,這條指令將ecx的值和ebp+10h
                                               ;的值相乘,將結果保存在ecx裏面
                                               ;ecx裏面此時保存的是全部堆對象所佔大小
00A236E4  add         ecx,dword ptr [ebp+8]  ;ebp+8所表明的的內存存放堆空間中第一個對象首地址,這裏將它與ecx相加
                                             ;結果保存在ecx裏面,此時ecx存放的是堆空間中最後一個對象後面的內存地址
                                             ;這麼作是爲了從最後一個對象開始析構
00A236E7  mov         dword ptr [ebp+8],ecx  ;將ecx的值保存到ebp+8所表明的內存
00A236EA  mov         dword ptr [ebp-4],0  
00A236F1  mov         edx,dword ptr [ebp+10h]  ;將對象個數給寄存器edx,至關於for循環裏面的循環技術變量
00A236F4  sub         edx,1  ;edx裏面的值減1
00A236F7  mov         dword ptr [ebp+10h],edx ;將edx裏面的值存放到ebp+10h所表明的的內存 
00A236FA  js          `eh vector destructor iterator'+6Dh (0A2370Dh) ;若是edx-1時爲父,跳轉到地址0A2370Dh處執行 00A236FC mov eax,dword ptr [ebp+8] ;將ebp+8內存的值(即最後一個堆對象後面的內存地址)給寄存器eax 00A236FF sub eax,dword ptr [ebp+0Ch] ;ebp+0ch所表明的內存存放對象大小,這裏用eax-對象大小 ;這裏依次獲得從最後一個堆對象到第一個堆對象首地址, ;存放到寄存器eax 00A23702 mov dword ptr [ebp+8],eax;將eax的值保存到ebp+8所表明的內存 00A23705 mov ecx,dword ptr [ebp+8];將堆對象首地址給ecx寄存器,做爲隱含參數傳遞給析構函數 00A23708 call dword ptr [ebp+14h] ;ebp+14h所表明的內存裏面存放的是析構函數地址,這裏調用析構函數 00A2370B jmp `eh vector destructor iterator'+51h (0A236F1h);跳轉到地址0A236F1h執行 ;後面是析構完對象以後的結束代碼

能夠看到,析構代理函數也是循環調用析構函數,以與構造對象相反的順序析構對象。


比較對單個堆對象調用delete和對堆對象數組調用delete[]能夠發現,兩種狀況最主要的差異是在用最後用delete釋放堆空間時,delete[]會對目標指針(即對delete來講,目標指針爲堆對象首地址,對delete[]來講,是堆中第一個堆對象首地址)進行減4調整.所以,若是是釋放單個對象堆空間,錯誤的使用delete[],那麼,執行中間檢測時,會判斷對象類型標記爲3,進行目標指針調整,結果會釋放錯誤的堆空間。同理,若是釋放對象數組,而錯誤的使用delet,那麼,執行中間檢測時,會判斷對象類型標記爲1,不進行目標指針調整,堆空間的釋放也會發生錯誤。

基本數據類型

下面是c++源碼:

class X {
private:
    int _x;
public:
    X(int xx = 0) : _x(xx) {}
    virtual ~X() {}
};

class Y  {
private:
    int _y;
public:
    Y(int yy = 0) : _y(yy) {}
    virtual ~Y() {}
};

class Z : public X, public Y {
private:
    int _z;
public:
    Z(int zz = 0) : _z(zz){}
    virtual ~Z() {}
};



int main()  {
    int* ip1 = new int[2];
    delete [] ip1;

    int* ip2 = new int;
    delete ip2;
    
  
}

c++代碼中ip1指向的是堆中基本類型int數組的首地址,ip1指向堆中單個基本類型int的首地址。

下面是mian函數裏面的彙編碼:

 28:     int* ip1 = new int[2];
00141A3E  push        8  ;將申請的堆空間大小壓棧,做爲參數傳遞給operator new
00141A40  call        operator new (141177h) ;調用operator new 
00141A45  add         esp,4  ;棧頂指針減4 釋放爲調用operator new傳參時分配的棧空間
00141A48  mov         dword ptr [ebp-104h],eax  ;寄存器eax裏面存有申請到的堆空間首地址,下面
                                                ;的代碼都是一些傳值操做
00141A4E  mov         eax,dword ptr [ebp-104h]  
00141A54  mov         dword ptr [ip1],eax  ;eax的值傳給了指針變量ip1
    29:     delete [] ip1;
00141A57  mov         eax,dword ptr [ip1]  ;將ip1的值(指向堆空間首地址)給寄存器eax
00141A5A  mov         dword ptr [ebp-0F8h],eax  ;下面是一些傳值操做
00141A60  mov         ecx,dword ptr [ebp-0F8h]  
00141A66  push        ecx  ;ecx裏面保存了堆空間首地址,這裏將ecx壓棧,爲調用delete傳參
00141A67  call        operator delete (141082h)  ;調用delete
00141A6C  add         esp,4  ;棧頂指針減4,釋放爲調用delete傳參時分配的棧空間
    30: 
    31:     int* ip2 = new int;
00141A6F  push        4 ;將申請的堆空間大小壓棧,做爲餐宿傳遞給operator new
00141A71  call        operator new (141177h);調用operator new  
00141A76  add         esp,4  ;棧頂指針減4 釋放爲調用operator new傳參時分配的棧空間
00141A79  mov         dword ptr [ebp-0ECh],eax  ;寄存器eax裏面存有申請到的堆空間首地址,下面
                                                ;的代碼都是一些傳值操做
00141A7F  mov         eax,dword ptr [ebp-0ECh]  
00141A85  mov         dword ptr [ip2],eax  ;eax的值給指針變量ip2
    32:     delete ip2;
00141A88  mov         eax,dword ptr [ip2]  ;將ip2的值(指向堆空間首地址)給寄存器eax
00141A8B  mov         dword ptr [ebp-0E0h],eax   ;下面是一些傳值操做
00141A91  mov         ecx,dword ptr [ebp-0E0h]  
00141A97  push        ecx  ;ecx裏面保存了堆空間首地址,這裏將ecx壓棧,爲調用delete傳參
00141A98  call        operator delete (141082h);調用delete  
00141A9D  add         esp,4 ;棧頂指針減4,釋放爲調用delete傳參時分配的棧空間

從彙編碼能夠看到,因爲基本數據類型沒有構造函數和析構函數,所以,這兩種狀況都只是簡單的調用new分配空間,調用delete釋放空間。而且還能夠看到,和堆中對象數組不一樣,堆中基本類型數組沒有在申請到的堆空間首地址處存放對象個數,ip1,ip2都直接指向的是各自申請到的堆空間首地址,正由於如此,對於基本類型,delete和delete[]效果同樣。

相關文章
相關標籤/搜索