函數調用過程中棧到底是怎麼壓入和彈出的?

一個程序在運行過程中,一個函數會調用另一個函數(比如遞歸),那麼函數在進入的時候都會往棧里壓點什麼東西,函數退出時會彈出點什麼東西,內層的函數是如何返回的,返回給外層函數的誰,返回到哪裡,內層函數是怎麼知道返回地址的?


湊合看吧親 ^_^

在main函數調用func_A的時候,首先在自己的棧幀中壓入函數返回地址,然後為func_A創建新棧幀並壓入系統棧

在func_A調用func_B的時候,同樣先在自己的棧幀中壓入函數返回地址,然後為func_B創建新棧幀並壓入系統棧

在func_B返回時,func_B的棧幀被彈出系統棧,func_A棧幀中的返回地址被「露」在棧頂,此時處理器按照這個返回地址重新跳到func_A代碼區中執行

在func_A返回時,func_A的棧幀被彈出系統棧,main函數棧幀中的返回地址被「露」在棧頂,此時處理器按照這個返回地址跳到main函數代碼區中執行

在實際運行中,main函數並不是第一個被調用的函數,程序被裝入內存前還有一些其他操作,上圖只是棧在函數調用過程中所起作用的示意圖

ESP:棧指針寄存器(extended stack pointer),其內存放著一個指針,該指針永遠指向系統棧最上面一個棧幀的棧頂

EBP:基址指針寄存器(extended base pointer),其內存放著一個指針,該指針永遠指向系統棧最上面一個棧幀的底部

函數棧幀:ESP和EBP之間的內存空間為當前棧幀,EBP標識了當前棧幀的底部,ESP標識了當前棧幀的頂部。

EIP:指令寄存器(extended instruction pointer), 其內存放著一個指針,該指針永遠指向下一條待執行的指令地址。

函數調用大致包括以下幾個步驟:

參數入棧:將參數從右向左依次壓入系統棧中

返回地址入棧:將當前代碼區調用指令的下一條指令地址壓入棧中,供函數返回時繼續執行

代碼區跳轉:處理器從當前代碼區跳轉到被調用函數的入口處

棧幀調整:具體包括

保存當前棧幀狀態值,已備後面恢複本棧幀時使用(EBP入棧)

將當前棧幀切換到新棧幀。(將ESP值裝入EBP,更新棧幀底部)

給新棧幀分配空間。(把ESP減去所需空間的大小,抬高棧頂)

————————————————分割線——————————————————————

以下內容代碼感謝原評論中 @陳中正 同學提供,答案經過重新編輯後原評論就不見鳥

void func_A(arg_A1, arg_A2);

void func_B(arg_B1, arg_B2);

int main(int argc, char *argv[], char **envp)

{

func_A(arg_A1, arg_A2);

}

void func_A(arg_A1, arg_A2)

{

var_A;

func_B(arg_B1, arg_B2);

}

void func_B(arg_B1, arg_B2)

{

var_B1;

var_B2;

}


一個程序在運行過程中,一個函數會調用另一個函數(比如遞歸),那麼函數在進入的時候都會往棧里壓點什麼東西

會壓入: 原來ebp的值, 參數, 以及調用函數的下一個指令地址

在調用一個函數時, 編譯器就計算好函數需要的空間, 然後esp = ebp-需要的空間, 通過ebp+偏移量來訪問. 在函數里調用另外一個函數時, 原來fun的ebp值壓棧

push ebp
ebp = esp
esp = ebp-需要的空間
藉此調用另外的函數

比如對於以上的程序, 布局是這樣的:

0x123 rbp(main)

0x11f c

0x11b b

0x117 a

0x113 rbp(foo) rsp(main,foo)

100000f30 &<調用fun下一個指令地址&>

函數退出時會彈出點什麼東西,內層的函數是如何返回的,返回給外層函數的誰,返回到哪裡,內層函數是怎麼知道返回地址的

退出時會做函數調用時的逆操作, 看偽代碼:

pop ebp
esp = ebp
esp = ebp+需要的空間

內層函數是通過之前已經壓棧了的調用函數時的下一條指令來得知返回地址的.

彙編與C之間的關係

最後, 看此圖就一目了然:


感覺一兩句話也說不太清楚...

推薦去看《深入理解計算機系統》

程序的機器級表示-&>過程那一節寫的蠻詳細的


參數和返回地址按照某個約定順序壓入棧中。然後跳到目標代碼。執行完之後。按照約定把這些信息清理出去,或者由調用方清理,看約定。把返回值放在寄存器或者棧中。恢復原來的位置,繼續執行。所以需要保留的現場信息全部在棧裡面。


Method Calls in Java

First, some information about the Stack, local variables parameters

? The Stack keeps the state of all active methods

(those that have been invoked but have not yet completed)

? When a method is called a new stack frame for it is added to

the top of the stack, this stack frame is where space for the

method』s local variables and parameters are allocated

? When a method returns, its stack frame is removed from the

top of the stack (space for its local vars and params is de-allocated)

? Space for local variables and parameters exist only while the

method is active (it has a stack frame on the Stack)

? local variables and parameters are only in scope when they are in

the top stack frame (when this method』s code is being executed)

An Example

public class TestMethodCalls {

public static void main(String[] args) {

Foo f1, f2;

int x=8;

f1 = new Foo(10);

f2 = new Foo(12);

f1.setVal(x);

x = f1.add(f2, x);

}

public class Foo {

private int x;

public Foo(int val) { x = val; }

public void setVal(int val) { x = val; }

public int getVal() { return x; }

public int plus(Foo f, int val) {

int resu<

result = f.getVal() + x + val;

return resu<

}

}


推薦閱讀:

進入計算機領域學習的學生已經太多了嗎?
軟體工程在工作中到底起多大作用?
Computer Science(計算機科學)對數學要求有多高?
美國在超級計算機領域落後了嗎?
怎樣成為一名硬體工程師?

TAG:演算法 | 編程 | 計算機 | 計算機科學 | 數據結構 |