C/C++ 函數調用過程,壓棧出棧


在x86的計算機系統中,內存空間中的棧主要用於保存函數的參數,返回值,返回地址,本地變量等。一切的函數調用都要將不同的數據、地址壓入或者彈出棧。因此,為了更好地理解函數的調用,我們需要先來看看棧是怎么工作的。

棧是什么?

簡單來說,棧是一種LIFO形式的數據結構,所有的數據都是后進先出。這種形式的數據結構正好滿足我們調用函數的方式:父函數調用子函數,父函數在前,子函數在后;返回時,子函數先返回,父函數后返回。棧支持兩種基本操作,push和pop。push將數據壓入棧中,pop將棧中的數據彈出並存儲到指定寄存器或者內存中。

這里是一個push操作的例子。假設我們有一個棧,其中黃色部分是已經寫入數據的區域,綠色部分是還未寫入數據的區域。現在我們將0x50壓入棧中:

// 將0x50的壓入棧
push $0x50
 
圖一:壓棧操作

我們再來看看pop操作的例子:

// 將0x50彈出棧
pop

 

 
圖二:出棧操作


這里有兩點需要注意的,第一,上面例子中棧的生長方向是從高地址到低地址的,棧是向下生長的,因此這里也用這種形式的棧;第二,pop操作后,棧中的數據並沒有被清空,只是該數據我們無法直接訪問。有了這些棧的基本知識,我們現在可以來看看在x86-32bit系統下,C語言函數是如何調用的了。

 

棧幀是什么?

棧幀,也就是stack frame,其本質就是一種棧,只是這種棧專門用於保存函數調用過程中的各種信息(參數,返回地址,本地變量等)。棧幀有棧頂和棧底之分,其中棧頂的地址最低,棧底的地址最高,SP(棧指針)就是一直指向棧頂的。在x86-32bit中,我們用 %ebp 指向棧底,也就是基址指針;用 %esp 指向棧頂,也就是棧指針。下面是一個棧幀的示意圖:

 
圖三:棧幀示意圖


一般來說,我們將 %ebp 到 %esp 之間區域當做棧幀(也有人認為該從函數參數開始,不過這不影響分析)。並不是整個棧空間只有一個棧幀,每調用一個函數,就會生成一個新的棧幀。在函數調用過程中,我們將調用函數的函數稱為“調用者(caller)”,將被調用的函數稱為“被調用者(callee)”。在這個過程中,1)“調用者”需要知道在哪里獲取“被調用者”返回的值;2)“被調用者”需要知道傳入的參數在哪里,3)返回的地址在哪里。同時,我們需要保證在“被調用者”返回后,%ebp%esp 等寄存器的值應該和調用前一致。因此,我們需要使用棧來保存這些數據。

為什么參數從右至左壓棧

1.C方式參數入棧順序(從右至左)的好處就是可以動態變化參數個數。通過棧堆分析可知,自左向右的入棧方式,最前面的參數被壓在棧底。這樣的話,除非知道參數個數,否則是無法通過棧指針的相對位移求得最左邊的參數。這樣就變成了左邊參數的個數不確定,正好和動態參數個數的方向相反。

2. 更符合習慣。 

采用這種順序,是為了讓程序員在使用C/C++的“函數參數長度可變”這個特性時更方便。

什么是“函數參數長度可變”?printf就是一個例子,它的參數的個數就是可變的,鏈接(1)中介紹了如何自己寫一個參數長度可變的函數。

看下面這句話 printf("%d %d %d",1,2,3),在采用從右向左的參數入棧順序時,參數出棧順序時"%d %d %d",1,2,3。

如果采用從左向右的入棧順序,則出棧順序變為3,2,1,"%d %d %d"。

普通函數、類成員函數、類虛函數的調用過程

  • 普通函數調用流程
    1. 開辟棧幀空間
    2. 函數參數從右至左進行壓棧
    3. 函數返回地址進行壓棧
    4. 函數局部變量進行壓棧
  • 普通成員函數調用流程(大體)

    1. 由於函數地址在編譯期間已確定,所以直接找到該函數地址
    2. this指針,作為隱含參數傳入該函數
    3. 之后的調用和普通函數調用方式一致
    4. 注意:如果該函數中,使用了實例的成員變量,由於this指針為null,程序會報錯。
  • 虛函數調用流程(大體)

    1. 查找this指針(也就是實例)的地址
    2. 根據this指針,查找虛函數表(函數指針數組)的地址
    3. 從虛函數表中,取出相應的函數地址

 

參考文檔: https://www.cnblogs.com/sddai/p/9762968.html

參考文檔:https://blog.csdn.net/hnyzyty/article/details/46427219


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM