隨想錄(函數壓棧)

來源:互聯網
上載者:User

【 聲明:著作權,歡迎轉載,請勿用於商業用途。  聯絡信箱:feixiaoxing @163.com】

 

    在編譯器編譯檔案的時候,軟體會根據程式本身的要求對函數作不同的壓棧處理。有的壓棧是按照從左至右進行壓棧,有的壓棧是按照從右至左進行壓棧,有的不壓棧、直接用寄存器代替,有的是需要被調用函數自身自己平衡堆棧。下面,我們就可以一個一個自己看看。首先,隨便寫一個函數,

int add(int a, int b){return a + b;}

 

    (1)從右至左壓棧

    _cdelc是編譯器預設的一種壓棧方式,在函數上定義不定義其實意義不大。不過,我們願意貼上代碼說明一下,

int __cdecl add(int a, int b){return a + b;}

    下面,在main函數裡面調用此add函數,我們看看對應的彙編是怎麼處理的,

14:       int p = add(2, 3);00401068   push        30040106A   push        20040106C   call        @ILT+0(_add) (00401005)00401071   add         esp,800401074   mov         dword ptr [ebp-4],eax15:       return 1;00401077   mov         eax,1

    從上面的代碼來看,3先壓棧,然後是資料2,很明顯的從右向左壓棧。

 

    (2)從左向右壓棧

    其實,在windows之前的編譯器是支援從左向右進行壓棧的,但是現在不支援了。比如說,如果你輸入下面這段代碼,

int __pascal add(int a, int b){return a + b;}

    此時,編譯器會給你貼上一個錯誤提示,error C4226: nonstandard extension used : '__pascal' is an obsolete keyword。提示說的很明白,__pascal是一個過時的關鍵字,現在不支援了。其實堆棧壓棧從左向右、還是從右向左其實無所謂。但是如果遇到的函數是變參的話,那麼此時就存在問題了。因為對於__pascal而言,最後一個參數不知道究竟是在ebp的哪個位移位置了?

 

    (3)用寄存器代替壓棧

    用寄存器代替資料壓棧是arm、powerpc等cpu使用的比較多的一種方法。因為用寄存器代替壓棧,主要是考慮到速度方面的原因。畢竟取資料、儲存資料相比較寄存器操作還是非常耗時間的,其此就是這兩種cpu的寄存器資源特別豐富。同樣,首先我們要用__fastcall裝飾一下函數,

int __fastcall add(int a, int b){return a + b;}

    那接下來,我們看看調用的時候發生了什麼變化,

14:       int p = add(2, 3);00401068   mov         edx,30040106D   mov         ecx,200401072   call        @ILT+10(_add) (0040100f)00401077   mov         dword ptr [ebp-4],eax15:       return 1;0040107A   mov         eax,1

    和上面的壓棧不同,這裡用edx儲存了資料3,用eax儲存了資料eax。畢竟寄存器運算要比記憶體運算快得多。

    (4)被調用和自行進行壓棧恢複
    __stdcall是我們這裡講到的最後一種壓棧模式。在函數壓棧的方面,他和__cdelc是一樣的,但是關鍵就在add函數結束的位置發生了變化。首先,我們需要用__stdcall裝飾了一下函數,

int __stdcall add(int a, int b){return a + b;}

    那麼此時函數彙編的時候,代碼發生了變化呢?

7:    int __stdcall add(int a, int b)8:    {00401020   push        ebp00401021   mov         ebp,esp00401023   sub         esp,40h00401026   push        ebx00401027   push        esi00401028   push        edi00401029   lea         edi,[ebp-40h]0040102C   mov         ecx,10h00401031   mov         eax,0CCCCCCCCh00401036   rep stos    dword ptr [edi]9:        return a + b;00401038   mov         eax,dword ptr [ebp+8]0040103B   add         eax,dword ptr [ebp+0Ch]10:   }0040103E   pop         edi0040103F   pop         esi00401040   pop         ebx00401041   mov         esp,ebp00401043   pop         ebp00401044   ret         8

    這裡的彙編代碼沒有什麼特別之處。但是最後一個ret 8是什麼意思呢?其實因為之前有兩個參數a和b,那麼8就是這兩個參數佔有的空間。此時ret b事實上就是讓ebp恢複到原來的空間。僅此而已。但是我們發現,可能這一步運行之後,ebp加了不是8,而是12,這又是為什麼呢?因為還有4個位元組的返回地址沒有加上呢。

 

 

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.