C/C++函数调用的压栈模型
? ? 函數(shù)調(diào)用的壓棧模型對于我們學(xué)習(xí)C語言非常重要,最直觀的體現(xiàn)在我們后面要學(xué)的函數(shù)的遞歸,函數(shù)的遞歸就充分利用的函數(shù)的壓棧模型。
? ??
? ? 當(dāng)函數(shù)從入口函數(shù)main函數(shù)開始執(zhí)行時(shí),編譯器會將我們操作系統(tǒng)的運(yùn)行狀態(tài),main函數(shù)的返回地址、main的參數(shù)、main函數(shù)中的變量、進(jìn)行依次壓棧;當(dāng)main函數(shù)開始調(diào)用fa()函數(shù)時(shí),編譯器此時(shí)會將main函數(shù)的運(yùn)行狀態(tài)進(jìn)行壓棧,再將fa()函數(shù)的返回地址、fa函數(shù)的參數(shù)、fa定義變量依次壓棧;當(dāng)fa調(diào)用fb的時(shí)候,編譯器此時(shí)會將fa函數(shù)的運(yùn)行狀態(tài)進(jìn)行壓棧,再將fb函數(shù)的返回地址、fb函數(shù)的參數(shù)、fb定義變量依次壓棧。
? ??
? ? 當(dāng)函數(shù)fb運(yùn)行完成后,fb所有的壓棧都會被編譯器釋放掉,編譯器再從棧中接收到fa函數(shù)的運(yùn)行狀態(tài)后,銜接調(diào)用fb函數(shù)之前的操作,繼續(xù)執(zhí)行,同理,fa執(zhí)行完后,編譯器對main函數(shù)的處理也相同。
? ? 一個函數(shù)可以在棧上分配內(nèi)存,也可以在堆上分配內(nèi)存,更可以在全局區(qū)域分配內(nèi)存,因此理解內(nèi)存從哪里來,對于我們函數(shù)參數(shù)的傳遞,變量的調(diào)用異常重要。
? ? fb函數(shù)在棧上分配的內(nèi)存,不能被fa和main函數(shù)所調(diào)用,因?yàn)樗鼤趂b函數(shù)執(zhí)行完后被編譯器釋放掉;而fb函數(shù)使用new和malloc在堆上分配的內(nèi)存或者全局區(qū)分配的內(nèi)存,只要不被程序員自己釋放掉,是可以被fa和main函數(shù)所調(diào)用的。
? ? 轉(zhuǎn)自:https://blog.csdn.net/m0_37717595/article/details/80368411
?
--------------------------------------------------------------------------------------
函數(shù)壓棧的過程(轉(zhuǎn) 知乎)
例如:
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函數(shù)調(diào)用func_A的時(shí)候,首先在自己的棧幀中壓入函數(shù)返回地址,然后為func_A創(chuàng)建棧幀并壓入系統(tǒng)棧;
在func_A調(diào)用func_B的時(shí)候,同樣先在自己的棧幀中壓入函數(shù)返回地址,然后為func_B創(chuàng)建新棧幀并壓入系統(tǒng)棧;
在func_B返回時(shí),func_B的棧幀被彈出系統(tǒng)棧,func_A棧幀中的返回地址被"露"在棧頂,此時(shí)處理器按照這個返回地址重新跳到func_A代碼區(qū)中執(zhí)行;
在func_A返回時(shí),func_A的棧幀被彈出系統(tǒng)棧,main函數(shù)棧幀中的返回地址被"露"在棧頂,此時(shí)處理器按照這個返回地址跳到main函數(shù)代碼區(qū)中執(zhí)行。
在實(shí)際運(yùn)行中,main函數(shù)并不是第一個被調(diào)用的函數(shù),程序被裝入內(nèi)存前還有一些其他操作,上圖只是棧在函數(shù)調(diào)用過程中所起作用的示意圖。
ESP:棧指針寄存器(extended stack pointer),其內(nèi)存放著一個指針,該指針永遠(yuǎn)指向系統(tǒng)棧最上面一個棧幀的棧頂。
EBP:基址指針寄存器(extended base pointer),其內(nèi)存放著一個指針,該指針永遠(yuǎn)指向系統(tǒng)棧最上面一個棧頂?shù)牡撞俊?/p>
函數(shù)棧幀:ESP和EBP之間的內(nèi)存空間為當(dāng)前棧幀,EBP標(biāo)識了當(dāng)前棧幀的底部,ESP標(biāo)識了當(dāng)前棧幀的頂部。
EIP:指令寄存器(extended instruction pointer),其內(nèi)存放著一個指針,該指針永遠(yuǎn)指向下一條待執(zhí)行的指令地址。
函數(shù)調(diào)用大致包括以下步驟:
1.參數(shù)入棧:將參數(shù)從右向左依次壓入系統(tǒng)棧中;
2.返回地址入棧:將當(dāng)前代碼區(qū)調(diào)用指令的下一條指令地址壓入棧中,供函數(shù)返回時(shí)繼續(xù)執(zhí)行;
3.代碼區(qū)跳轉(zhuǎn):處理器從當(dāng)前代碼區(qū)跳轉(zhuǎn)到被調(diào)用函數(shù)的入口處。
4.棧幀調(diào)整:具體包括:
? ? 保存當(dāng)前棧幀狀態(tài)值,已備后面恢復(fù)本棧幀時(shí)使用(EBP入棧);
? ? 將當(dāng)前棧幀切換到新棧幀。(將ESP值裝入EBP,更新棧幀底部)
? ? 給新棧幀分配空間。(把ESP減去所需空間的大小,抬高棧頂)
?
轉(zhuǎn)自:http://www.cnblogs.com/wsw-seu/p/8278547.html
?
總結(jié)
以上是生活随笔為你收集整理的C/C++函数调用的压栈模型的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Linux写时拷贝技术(copy-on-
- 下一篇: C/C++ strlen函数为什么不能传