C/C++函数调用的压栈模型
? ? 函數調用的壓棧模型對于我們學習C語言非常重要,最直觀的體現在我們后面要學的函數的遞歸,函數的遞歸就充分利用的函數的壓棧模型。
? ??
? ? 當函數從入口函數main函數開始執行時,編譯器會將我們操作系統的運行狀態,main函數的返回地址、main的參數、main函數中的變量、進行依次壓棧;當main函數開始調用fa()函數時,編譯器此時會將main函數的運行狀態進行壓棧,再將fa()函數的返回地址、fa函數的參數、fa定義變量依次壓棧;當fa調用fb的時候,編譯器此時會將fa函數的運行狀態進行壓棧,再將fb函數的返回地址、fb函數的參數、fb定義變量依次壓棧。
? ??
? ? 當函數fb運行完成后,fb所有的壓棧都會被編譯器釋放掉,編譯器再從棧中接收到fa函數的運行狀態后,銜接調用fb函數之前的操作,繼續執行,同理,fa執行完后,編譯器對main函數的處理也相同。
? ? 一個函數可以在棧上分配內存,也可以在堆上分配內存,更可以在全局區域分配內存,因此理解內存從哪里來,對于我們函數參數的傳遞,變量的調用異常重要。
? ? fb函數在棧上分配的內存,不能被fa和main函數所調用,因為它會在fb函數執行完后被編譯器釋放掉;而fb函數使用new和malloc在堆上分配的內存或者全局區分配的內存,只要不被程序員自己釋放掉,是可以被fa和main函數所調用的。
? ? 轉自:https://blog.csdn.net/m0_37717595/article/details/80368411
?
--------------------------------------------------------------------------------------
函數壓棧的過程(轉 知乎)
例如:
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; }在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),其內存放著一個指針,該指針永遠指向下一條待執行的指令地址。
函數調用大致包括以下步驟:
1.參數入棧:將參數從右向左依次壓入系統棧中;
2.返回地址入棧:將當前代碼區調用指令的下一條指令地址壓入棧中,供函數返回時繼續執行;
3.代碼區跳轉:處理器從當前代碼區跳轉到被調用函數的入口處。
4.棧幀調整:具體包括:
? ? 保存當前棧幀狀態值,已備后面恢復本棧幀時使用(EBP入棧);
? ? 將當前棧幀切換到新棧幀。(將ESP值裝入EBP,更新棧幀底部)
? ? 給新棧幀分配空間。(把ESP減去所需空間的大小,抬高棧頂)
?
轉自:http://www.cnblogs.com/wsw-seu/p/8278547.html
?
總結
以上是生活随笔為你收集整理的C/C++函数调用的压栈模型的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Linux写时拷贝技术(copy-on-
- 下一篇: C/C++ strlen函数为什么不能传