前沿:
? ? ? ? 可能很多人也是第一次聽說(shuō)函數(shù)棧幀這個(gè)詞,想問(wèn)什么是函數(shù)棧幀,理解函數(shù)棧幀有什么作用,函數(shù)棧幀的創(chuàng)建銷毀是什么呢?這章節(jié)我們就來(lái)了解一下c語(yǔ)言中函數(shù)棧幀的創(chuàng)建和銷毀。
思維導(dǎo)圖:
目錄
?
一、什么是函數(shù)棧幀
?1.1?? 函數(shù)棧幀:
1.2? 棧:
?1.3寄存器:
? ?1.3.1? 寄存器的概念是:
1.3.2寄存器的功能:
1.3.3相關(guān)的寄存器為:
1.3.4 相關(guān)匯編命令:
二、函數(shù)棧幀的創(chuàng)建和銷毀
2.1首先了解運(yùn)行時(shí)棧堆的使用:
2.2 代碼演示棧幀創(chuàng)建銷毀:
2.2.1首先對(duì)這個(gè)代碼進(jìn)行調(diào)試:?編輯
2.2.2 代碼反匯編:
2.2.3函數(shù)棧幀的創(chuàng)建:
2.2.4函數(shù)棧幀的銷毀:
三、總結(jié):
一、什么是函數(shù)棧幀
?1.1?? 函數(shù)棧幀:
1.2? 棧:

?1.3寄存器:
~同樣的我們也需要了解一下什么是寄存器。
? ?1.3.1? 寄存器的概念是:
? ? ? ?1、寄存器是CPU內(nèi)部用來(lái)存放數(shù)據(jù)的一些小型存儲(chǔ)區(qū)域,用來(lái)暫時(shí)存放參與運(yùn)算的數(shù)據(jù)和運(yùn)算結(jié)果。其實(shí)寄存器就是一種常用的時(shí)序邏輯電路,但這種時(shí)序邏輯電路只包含存儲(chǔ)電路。寄存器的存儲(chǔ)電路是由鎖存器或觸發(fā)器構(gòu)成的,因?yàn)橐粋€(gè)鎖存器或觸發(fā)器能存儲(chǔ)1位二進(jìn)制數(shù),所以由N個(gè)鎖存器或觸發(fā)器可以構(gòu)成N位寄存器。寄存器是中央處理器內(nèi)的組成部分。寄存器是有限存儲(chǔ)容量的高速存儲(chǔ)部件,它們可用來(lái)暫存指令、數(shù)據(jù)和位址。
1.3.2寄存器的功能:
①清除數(shù)碼:將寄存器里的原有數(shù)碼清除。
②接收數(shù)碼:在接收脈沖作用下,將外輸入數(shù)碼存入寄存器中。
③存儲(chǔ)數(shù)碼:在沒有新的寫入脈沖來(lái)之前,寄存器能保存原有數(shù)碼不變。
④輸出數(shù)碼:在輸出脈沖作用下,才通過(guò)電路輸出數(shù)碼。
1.3.3相關(guān)的寄存器為:
⑤?eip:指令寄存器,保存當(dāng)前指令的下一條指令的地址。
1.3.4 相關(guān)匯編命令:
二、函數(shù)棧幀的創(chuàng)建和銷毀
2.1首先了解運(yùn)行時(shí)棧堆的使用:
函數(shù)被調(diào)用時(shí),系統(tǒng)會(huì)在棧區(qū)為該函數(shù)開辟一塊??臻g,這個(gè)??臻g就是該函數(shù)的函數(shù)棧幀,而以用到的寄存器esp 和 ebp , ebp 記錄的是棧底的地址, esp 記錄的是棧頂 的地址。
2.2 代碼演示棧幀創(chuàng)建銷毀:
#include <stdio.h>
int Add(int x, int y)
{ int z = 0; z = x + y; return z; }
int main()
{
int a = 3;
int b = 5;
int ret = 0;
ret = Add(a, b);
printf("%d\n", ret);
return 0;
}
?代碼是很簡(jiǎn)單的主要是為了我們來(lái)進(jìn)行深入的了解棧幀的創(chuàng)建和銷毀。
2.2.1首先對(duì)這個(gè)代碼進(jìn)行調(diào)試:
? ? ? 我們剛開始就已經(jīng)說(shuō)啦,main函數(shù)是被invoke_main函數(shù)調(diào)用的,invoke_main函數(shù)被那個(gè)函數(shù)調(diào)用我們先不去了解。但函數(shù)棧幀的創(chuàng)建和銷毀過(guò)程,在不同的編譯器上實(shí)現(xiàn)的方法大同小異,可能不同的編譯器main調(diào)用函數(shù)不一樣但是方法都是相同的。這樣我們就可以知道invoke_main函數(shù)肯定存在自己的棧幀,相同的main函數(shù)和Add函數(shù)也會(huì)存在自己的棧幀,每個(gè)函數(shù)棧幀都有自己的 ebp 和 esp 來(lái)維護(hù)棧幀空間。
2.2.2 代碼反匯編:
2.2.3函數(shù)棧幀的創(chuàng)建:
? 上面代碼的反匯編們來(lái)幫助我們進(jìn)行理解?
00 BE1820? ? push? ? ? ? ? ?ebp???????? // 把 ebp 寄存器中的值進(jìn)行壓棧,此時(shí)的 ebp 中存放的是invoke_main 函數(shù)棧幀的 ebp , esp-400 BE1821? ? mov? ? ? ? ? ? ebp , esp ???????? //move 指令會(huì)把 esp 的值存放到 ebp 中,相當(dāng)于產(chǎn)生了 main 函數(shù)的 ebp,這個(gè)值就是 invoke_main 函數(shù)棧幀的 esp00 BE1823? ? sub? ? ? ? ? ? ?esp , 0E4 h???????? //sub 會(huì)讓 esp 中的地址減去一個(gè) 16 進(jìn)制數(shù)字 0xe4, 產(chǎn)生新的esp,此時(shí)的 esp 是 main 函數(shù)棧幀的 esp ,此時(shí)結(jié)合上一條指令的 ebp 和當(dāng)前的 esp , ebp 和 esp 之間維護(hù)了一 個(gè)塊棧空間,這塊棧空間就是為main 函數(shù)開辟的,就是 main 函數(shù)的棧幀空間,這一段空間中將存儲(chǔ) main 函數(shù) 中的局部變量,臨時(shí)數(shù)據(jù)已經(jīng)調(diào)試信息等。00 BE1829? ? push? ? ? ? ? ?ebx // 將寄存器 ebx 的值壓棧, esp-400 BE182A? ? push? ? ? ? ? ?esi // 將寄存器 esi 的值壓棧, esp-400 BE182B push edi // 將寄存器 edi 的值壓棧, esp-4// 上面 3 條指令保存了 3 個(gè)寄存器的值在棧區(qū),這 3 個(gè)寄存器的在函數(shù)隨后執(zhí)行中可能會(huì)被修改,所以先保存寄存器原來(lái)的值,以便在退出函數(shù)時(shí)恢復(fù)。// 下面的代碼是在初始化 main 函數(shù)的棧幀空間。//1. 先把 ebp-24h 的地址,放在 edi 中//2. 把 9 放在 ecx 中//3. 把 0xCCCCCCCC 放在 eax 中//4. 將從 edp-0x2h 到 ebp 這一段的內(nèi)存的每個(gè)字節(jié)都初始化為 0xCC00 BE182C? ? lea? ? ? ? ? ? ? ? edi ,[ ebp - 24 h ]00 BE182F? ? mov? ? ? ? ? ? ? ecx , 900 BE1834? ? mov? ? ? ? ? ? ? eax , 0 CCCCCCCCh?![]()
?這里我們知道一個(gè)小的知識(shí)點(diǎn):
? ? ?為什么局部變量的時(shí)候一定要初始化,因?yàn)槿魺o(wú)初始化系統(tǒng)在內(nèi)存中初始化都為0xCC,產(chǎn)生的隨機(jī)值我們是不知道的,所以一定要初始化你定的數(shù)。
?????????????????int a = 3;
00 BE183B ???????????mov??????????? ? dword ptr [ ebp - 8 ], 3 // 將 3 存儲(chǔ)到 ebp-8 的地址處, ebp-8 的位置其就是 變量????????????????int b = 5 ;00 BE1842? ? ? ? ? ? mov ?????????????dword ptr [ ebp - 14 h ], 5 // 將 5 存儲(chǔ)到 ebp-14h 的地址處, ebp-14h 的位置 其實(shí)是b 變量????????????????int ret = 0 ;00 BE1849? ? ? ? ? ? mov ?????????????dword ptr [ ebp - 20 h ], 0 // 將 0 存儲(chǔ)到 ebp-20h 的地址處, ebp-20h 的位 置其實(shí)是ret 變量// 以上匯編代碼表示的變量 a,b,ret 的創(chuàng)建和初始化,這就是局部的變量的創(chuàng)建和初始化// 其實(shí)是局部變量的創(chuàng)建時(shí)在局部變量所在函數(shù)的棧幀空間中創(chuàng)建的// 調(diào)用 Add 函數(shù)????????????????ret = Add ( a , b );// 調(diào)用 Add 函數(shù)時(shí)的傳參// 其實(shí)傳參就是把參數(shù) push 到棧幀空間中00 BE1850? ? ? ? ? ? mov? ? ? ? ? ? ? ??eax , dword ptr [ ebp - 14 h ] ???????? // 傳遞 b ,將 ebp-14h 處放的 5 放在 eax 寄存器中00 BE1853? ? ? ? ? ?push ???????????????eax ???????? // 將 eax 的值壓棧, esp-400 BE1854? ? ? ? ?? mov ????????????????ecx , dword ptr [ ebp - 8 ] ???????? // 傳遞 a ,將 ebp-8 處放的 3 放在 ecx 寄存器中00 BE1857? ? ? ? ?? push ???????????????ecx ???????? // 將 ecx 的值壓棧, esp-4//跳轉(zhuǎn)調(diào)用函數(shù)00 BE1858? ? ? ? ?? call????????????????? 00 BE10B400 BE185D? ? ? ? ? add???????????????? esp , 800 BE1860? ? ? ? ??mov ????????????????dword ptr [ ebp - 20 h ], eax
Add的傳參?:?
????????//調(diào)用 Add 函數(shù)????????ret = Add ( a , b );????????//調(diào)用 Add 函數(shù)時(shí)的傳參????????//其實(shí)傳參就是把參數(shù) push 到棧幀空間中,這里就是函數(shù)傳參00 BE1850 ????????mov ????????eax , dword ptr [ ebp - 14 h ]???????? // 傳遞 b ,將 ebp-14h 處放的 5 放在 eax 寄存器中00 BE1853???????? push ????????eax ???????? // 將 eax 的值壓棧, esp-400 BE1854 ????????mov???????? ecx , dword ptr [ ebp - 8 ] ???????? // 傳遞 a ,將 ebp-8 處放的 3 放在 ecx 寄存器中00 BE1857 ????????push ????????ecx ???????? // 將 ecx 的值壓棧, esp-4// 跳轉(zhuǎn)調(diào)用函數(shù)00 BE1858 ????????call???????? 00 BE10B400 BE185D ????????add ????????esp , 800 BE1860 ????????mov ????????dword ptr [ ebp - 20 h ], eax?
?函數(shù)調(diào)用過(guò)程:
00 BE1858 ????????call ???????? 00 BE10B400 BE185D ????????add???????? esp , 800 BE1860 ????????mov ????????dword ptr [ ebp - 20 h ], eax
?int Add(int x, int y)
{00 BE1760 ????????push???????? ebp ???????? // 將 main 函數(shù)棧幀的 ebp 保存 ,esp-400 BE1761 ????????mov ????????ebp , esp ???????? // 將 main 函數(shù)的 esp 賦值給新的 ebp , ebp 現(xiàn)在是 Add 函數(shù)的 ebp00 BE1763???????? sub ????????esp , 0 CCh ???????? // 給 esp-0xCC ,求出 Add 函數(shù)的 esp00 BE1769 ????????push ????????ebx ???????? // 將 ebx 的值壓棧 ,esp-400 BE176A ????????push ????????esi ???????? // 將 esi 的值壓棧 ,esp-400 BE176B ????????push???????? edi???????? // 將 edi 的值壓棧 ,esp-4int z = 0 ;00 BE176C ????????mov ????????dword ptr [ ebp - 8 ],???????? ? // 將 0 放在 ebp-8 的地址處,其實(shí)就是創(chuàng)建 zz = x + y ;// 接下來(lái)計(jì)算的是 x+y ,結(jié)果保存到 z 中00 BE1773 ????????mov ????????eax , dword ptr [ ebp + 8 ] // 將 ebp+8 地址處的數(shù)字存儲(chǔ)到 eax 中00 BE1776 ????????add ????????eax , dword ptr [ ebp + 0 Ch ] // 將 ebp+12 地址處的數(shù)字加到 eax 寄存中00 BE1779 ????????mov ????????dword ptr [ ebp - 8 ], eax ???????? // 將 eax 的結(jié)果保存到 ebp-8 的地址處,其實(shí) 就是放到z 中return z ;00 BE177C???????? mov ????????eax , dword ptr [ ebp - 8 ] ???????? // 將 ebp-8 地址處的值放在 eax 中,其實(shí)就是把z 的值存儲(chǔ)到 eax 寄存器中,這里是想通過(guò) eax 寄存器帶回計(jì)算的結(jié)果,做函數(shù)的返回值。}00 BE177F ????????pop ????????edi00 BE1780 ????????pop ????????esi00 BE1781 ????????pop ????????ebx00 BE1782???????? mov ????????esp , ebp00 BE1784 ????????pop ????????ebp00 BE1785???????? ret
2.2.4函數(shù)棧幀的銷毀:
1、要知道我們創(chuàng)建的棧就好比我們住的賓館,當(dāng)不用的時(shí)候要退房間,棧也一樣,當(dāng)調(diào)用完成后要進(jìn)行銷毀。而如何銷毀呢我們可以看一下。
00BE177F???????? pop ????????edi ????????//在棧頂彈出一個(gè)值,存放到edi中,esp+4
00 BE1780 ????????pop ????????esi ???????? // 在棧頂彈出一個(gè)值,存放到 esi 中, esp+400 BE1781 ????????pop ????????ebx ???????? // 在棧頂彈出一個(gè)值,存放到 ebx 中, esp+400 BE1782 ????????mov ????????esp , ebp ???????? //再將 Add 函數(shù)的 ebp 的值賦值給 esp ,相當(dāng)于回收了 Add 函數(shù)的棧幀空間00 BE1784 ????????pop???????? ebp???????? // 彈出棧頂?shù)闹荡娣诺?/span> ebp ,棧頂此時(shí)的值恰好就是 main 函數(shù)的 ebp, esp+4,此時(shí)恢復(fù)了 main 函數(shù)的棧幀維護(hù), esp 指向 main 函數(shù)棧幀的棧頂, ebp 指向了 main 函數(shù)棧幀的棧底。00 BE1785???????? ret ???????? //ret 指令的執(zhí)行,首先是從棧頂彈出一個(gè)值,此時(shí)棧頂?shù)闹稻褪?/span> call 指 令下一條指令的地址,此時(shí)esp+4 ,然后直接跳轉(zhuǎn)到 call 指令下一條指令的地址處,繼續(xù)往下執(zhí)行。

?00 BE185D ????????add ????????esp , 8 //esp 直接 +8 ,相當(dāng)于跳過(guò)了 main 函數(shù)中壓棧的 a'和 b'00 BE1860???????? mov ????????dword ptr [ ebp - 20 h ], eax // 將 eax 中值,存檔到 ebp-0x20 的地址處,其實(shí)就是存儲(chǔ)到main 函數(shù)中 ret 變量中,而此時(shí) eax 中就是 Add 函數(shù)中計(jì)算的 x 和 y 的和,可以看出來(lái),本次函數(shù)的返回值是由eax 寄存器帶回來(lái)的。程序是在函數(shù)調(diào)用返回之后,在 eax 中去讀取返回值的。
?到這里函數(shù)棧幀的創(chuàng)建和銷毀就結(jié)束了。文章來(lái)源:http://www.zghlxwxcb.cn/news/detail-466391.html
三、總結(jié):
學(xué)完想必我們就好理解一下幾個(gè)問(wèn)題啦:文章來(lái)源地址http://www.zghlxwxcb.cn/news/detail-466391.html
到了這里,關(guān)于c語(yǔ)言(函數(shù)棧幀的創(chuàng)建和銷毀)的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!