鏈接:https://www.zhihu.com/question/22444939/answer/22200552
來源:知乎
著作權歸作者所有,轉載請聯系作者獲得授權。

在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;
}