1. 程式人生 > >函式的呼叫過程詳解———棧幀的建立和銷燬

函式的呼叫過程詳解———棧幀的建立和銷燬

●回顧內容:

函式的定義:函式是一個程式中的部分程式碼,由一個或多個語句組成,它的功能是實現某些特定的任務。函式相對於其他程式碼來說具備相對的獨立性。

函式的呼叫:在某個函式內部,使用另一個函式來完成相關的任務,這個過程叫做函式呼叫。

那麼函式是如何呼叫的呢?分析一段簡單的程式碼:

#include<stdio.h>

Add(int x,int y)
{
    int z=0;
    z = x+y;
    return z;
}

int main()
{
    int a=10;
    int b=20;
    int ret=0;
    ret = Add(a,b);
    printf("%d\n",ret);

    return 0;
}

除錯程式時檢視呼叫堆疊:

  可以看出main函式其實是在mainCRTStartup函式中呼叫的。

(在VS編譯器中可以看出其實main函式是在_tmainCRTStartup函式中呼叫的,而_tmainCRTStartup函式是在mainCRTStartup中   呼叫的。) 

1》每一次函式的呼叫都是一個過程,這個過程叫做函式的呼叫過程

2》在呼叫函式的過程中,要為函式開闢棧空間,這塊空間叫做函式棧幀。棧的使用方向是由高地址向低地址使用的。

3》為了維護棧幀,需要用到ebp和esp兩個暫存器。在函式呼叫過程中ebp存放維護這個棧幀的棧底指標,esp存放棧頂指標

4》main函式的棧幀維護:

       在呼叫main函式時,編譯器會先呼叫mainCRTStartup函式,在呼叫時編譯器會主動為mainCRTStartup函式開闢一塊棧幀,然後用ebp和esp分別指向這塊棧幀的棧頂和棧底。

 

●為main函式的呼叫做好準備工作後,對應到彙編程式碼來研究函式的呼叫過程

1.main函式棧幀的建立:要呼叫main函式就要為main函式建立棧幀

main函式棧幀的建立過程:

2.Add函式的呼叫過程:

         1》

main函式建立好棧幀並建立了局部變數後就呼叫Add函式,要呼叫Add函式,就要為Add開闢函式棧幀做準備

值得注意的是,在呼叫Add函式之前:

   1>編譯器先為區域性變數a和b建立了一份臨時拷貝,相當於函式的傳參;

   2>呼叫call指令,裡面存了call指令下一條指令的地址。這樣可以儲存main函式棧幀的狀態,也方便呼叫完Add之後返回main函式的棧幀。這可以認為是現場保護和現場恢復

       2》當彙編程式碼執行到call指令時,開啟記憶體,按F11進入函式,在監視視窗會發現call指令裡存了call指令下一條指令的地址,此時彙編程式碼來到了這裡

 

      3》然後再按F11,就進入了Add函式的執行程式碼處,現在就開始了Add真正的呼叫過程

3.Add函式呼叫結束銷燬Add函式的函式棧幀並返回main函式

 

          執行完ret,彙編程式碼跳轉到剛才call指令的下一條指令的地址,

4.列印ret的值,main結束之後銷燬main函式的棧幀

      程式碼執行到這裡,整個函式的呼叫已經全部結束,簡單來說,函式的呼叫過程就是函式棧幀的建立和銷燬的過程。上面的程式碼內容我是用vc6.0演示的,不同的編譯器還是有些差異,但是思想都是一致的。