成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專欄INFORMATION COLUMN

【C語言知識精講②】函數(shù)棧幀的創(chuàng)建和銷毀(全程圖解)

davidac / 3123人閱讀

摘要:這里分塊講解六函數(shù)棧幀的銷毀過程一解析的作用是將棧頂?shù)臄?shù)據(jù)彈出,彈出數(shù)據(jù)儲存到相應寄存器中。

?前言?

讀完這篇博客,你可以明白什么?

①局部變量到底是怎么在棧上創(chuàng)建的?

②為什么局部變量不初始化為隨機值?

③函數(shù)是怎么傳參的?傳參的先后順序是什么?

④形參和實參是什么關(guān)系?

⑤函數(shù)調(diào)用是怎么實現(xiàn)的?

⑥函數(shù)調(diào)用后是怎么返回的?? ?

?在這篇博客里,我將帶領大家利用反匯編從底層上理解,不用擔心,都是零基礎入門的。當你學完這篇博客去面試,面試官會非常高興,覺得這小伙子真??。所以學起來吧!

???作者概況:? 就讀南京郵電大學努力學習的大一小伙

???聯(lián)系方式:2879377052(QQ小號)? ? ? ? ? ? ?

???資源推薦:C語言從入門到進階

???今日書籍分享:??《深入理解計算機系統(tǒng)》? ? ? ? ? ? ? ? ???

目錄

一、寄存器

二、main函數(shù)的調(diào)用

三、準備階段

?四、main棧幀的創(chuàng)建分析

?五、add函數(shù)棧幀的創(chuàng)建

?六、add函數(shù)棧幀的銷毀?

七、main函數(shù)棧幀的銷毀

八、完整反匯編代碼

九、后記


一、寄存器

在C語言中我們可以把寄存器當成指針來看待,他可以指向一塊空間,也可以用來存儲數(shù)據(jù)?,F(xiàn)在向大家介紹以下幾種基本寄存器

參考博客:函數(shù)棧幀的創(chuàng)建和銷毀(圖解)

寄存器名稱功能
①eax"累加器"(accumulator), 它是很多加法乘法指令的缺省寄存器。
②ebx"基地址"(base)寄存器, 在內(nèi)存尋址時存放基地址。
③ecx計數(shù)器(counter),計數(shù)寄存器,用于循環(huán)操作,比如重復的字符存儲操作,或者數(shù)字統(tǒng)計。
④edx作為EAX的溢出寄存器,總是被用來放整數(shù)除法產(chǎn)生的余數(shù)。
⑤esi源變址寄存器,主要用于存放存儲單元在段內(nèi)的偏移量。通常在內(nèi)存操作指令中作為“源地址指針”使用。
⑥edi目的變址寄存器,主要用于存放存儲單元在段內(nèi)的偏移量。
⑦esp?棧指針寄存器(extended stack pointer),其內(nèi)存放著一個指針,該指針永遠指向棧最上面一個棧幀的棧頂。esp用于堆棧操作,被形象地稱為棧頂指針。堆棧的頂部是地址小的區(qū)域,壓入堆棧的數(shù)據(jù)越多,esp也就越來越小。在32位平臺上,esp每次減少4字節(jié)。
⑧ebp?基址指針寄存器(extended base pointer),其內(nèi)存放著一個基址指針,該指針永遠指向系統(tǒng)棧最上面一個棧幀的底部?;分羔槪直恍螀⒎Q作棧底指針,一般與esp配合使用,可以存取某時刻的esp,這個時刻就是進入一個函數(shù)內(nèi)后,CPU會將esp的值賦給ebp,此時就可以通過ebp對棧進行操作。

二、main函數(shù)的調(diào)用

main函數(shù)其實也是被其他函數(shù)調(diào)用的,函數(shù)調(diào)用關(guān)系如下:

mainCRTStartup? →? ?__tmainCRTStartup? →? main?

?

(在調(diào)用堆棧中即可觀察到)?

三、準備階段

①首先我們寫一段簡單的代碼,將代碼的每個過程拆分的盡可能簡單以便于我們對過程的觀察

int add(int x,int y){	int z = 0;	z = x + y;	return z;}int main(){	int a = 10;	int b = 20;	int c = add(a,b);	return 0;}

?②在調(diào)試狀態(tài)下轉(zhuǎn)入反匯編模式

?

(完整連續(xù)反匯編代碼請看文章結(jié)尾。這里分塊講解)?

?看不懂沒關(guān)系,現(xiàn)在逐一解釋

我們知道,函數(shù)的調(diào)用都需要在棧區(qū)上開辟空間,那么我們先來解答幾個問題:

1.什么是棧?

【答】棧是一種數(shù)據(jù)結(jié)構(gòu),它按照后進先出的原則存儲數(shù)據(jù),先進入的數(shù)據(jù)被壓入棧底,最后的數(shù)據(jù)在棧頂,需要讀數(shù)據(jù)的時候從棧頂開始彈出數(shù)據(jù)。棧區(qū)內(nèi)存空間的使用是從高地址向低地址處使用的。

2.什么是壓棧?什么是出棧?

【答】一個形象的比喻就是機槍彈夾。壓棧的過程就是壓入一個元素,相當于向機槍彈夾壓入子彈;出棧的過程就是彈出一個元素,相當于子彈彈出來的過程。這正好對應了棧的結(jié)構(gòu)特點——先進入的數(shù)據(jù)被壓在棧底,后進入的數(shù)據(jù)在棧頂。

3.為什么一個數(shù)據(jù)在內(nèi)存中是“倒著”存放的(注意顯示的是16進制)?

【答】數(shù)據(jù)的存儲涉及大小端問題:

①什么是大小端

>大端(存儲)模式是指數(shù)據(jù)的低位保存在內(nèi)存的高地址中,而數(shù)據(jù)的高位,保存在內(nèi)存的低地址中。
>小端(存儲)模式是指數(shù)據(jù)的低位保存在內(nèi)存的低地址中,而數(shù)據(jù)的高位,,保存在內(nèi)存的高地址中。

?

?②為什么有大小端

存放的內(nèi)容大于1個字節(jié),必然存在著如何將多字節(jié)安排的問題。因此就出現(xiàn)了大小端。具體大小端取決于編譯器,一般為小端存儲。?

?四、main棧幀的創(chuàng)建分析

1.我們之前提到,main函數(shù)是由__tmianCRTStartup函數(shù)調(diào)用的,所以在創(chuàng)建main函數(shù)棧幀前,ebp和esp寄存器維護--tmainCRTStartup的棧區(qū),分別存放指向棧幀的棧頂和棧底。

2.同時注意,棧區(qū)上內(nèi)存的使用是從高地址向低地址處使用的

?

【過程一:??push? ?ebp?

?【解釋】push指令的作用:它首先減小esp的值,再將源操作數(shù)復制到棧地址,每次esp地址減去四字節(jié)。最終效果就是在棧頂壓入一個元素,元素的值為ebp的地址。(4個字節(jié))

【補充】內(nèi)容不隨指令執(zhí)行而變化的操作數(shù)為源操作數(shù),內(nèi)容隨執(zhí)行指令而改變的操作數(shù)為目標操作數(shù)。

?

?

?

?【過程二:? mov? ? ebp,esp?】

?【解釋】mov指令作用:將一個數(shù)據(jù)從源地址傳送到目標地址,源操作地址的內(nèi)容不變。最終效果是將esp的地址賦值給ebp。寄存器指向的空間發(fā)生改變。

?

?

?【過程三:? ?sub? ? esp,0E4h】

?【解釋】sub指令的作用:減操作指令,地址減去相應的數(shù)值。最終效果就是esp的地址減去0E4h。

?

?

?【過程四:push ebx,esi,edi】

?【解釋】前面我們提到過push的過程就壓入元素的過程。那壓入這三個元素有什么用呢?等會就明白。

?

??【過程五:lea? edi,[ebp- 0E4hh]? /? mov ? ecx,9? /? mov ? eax, 0CCCCCCCCh】

【解釋】Load Effective Address,即裝入有效地址的意思,它的操作數(shù)就是地址。在這里的效果就是將ebp+FFFFFF1Ch的值賦給edi。勾選“顯示符號名后可以發(fā)現(xiàn):

?

?ebp - 0E4h不正是當初esp - 0E4h時的地址嗎?

?

?執(zhí)行mov指令后,我們可以發(fā)現(xiàn)寄存器eax,ecx的值發(fā)生變化。

?

?【過程六:?rep stos ? ?dword ptr es:[edi]?】???

?【解釋】rep指令的作用是:重復后面的指令。stos指令的作用是將eax中的值拷貝到es:edi指向的地址。ecx表示重復操作的次數(shù)。dword表示4個字節(jié)。所以整句指令的作用是:從edi開始,向高地址方向,將ecx個4字節(jié)內(nèi)存全部修改為eax的值。

?

?(可以看到,執(zhí)行操作后edi上相應數(shù)量的四字節(jié)內(nèi)存被賦值為cc cc cc cc。這也解釋了為什么我們不初始化,變量默認的初始值為cc cc cc cc)

?

?

(完整連續(xù)反匯編代碼請看文章結(jié)尾。這里分塊講解)??

【過程七:mov? ? ?dword? ptr? [ebp - 14h] ,14h】?

【解析】將ebp - 14地址處的四字節(jié)內(nèi)容修改為14h(10進制中的20),也就是完成了給b賦值為20的動作。下一條語句同理。

?

?

??至此main函數(shù)的棧幀創(chuàng)建的準備階段完成。我們準備進入add函數(shù)

【過程八:mov? ?eax,dword ptr [ebp -?8h]? ?/? ?push ? ?eax】

【解析】將ebp - 14地址處的數(shù)值儲存到eax處;壓棧,將一個數(shù)值與eax相等的元素壓入棧中。ebp - 14這個地址好像似曾相識,沒錯,這正是b的地址,所以這條語句的作用實際上即使將b的值傳到eax中。同理,a的值被存儲到ecx中去。

(實際上,上述過程解釋了函數(shù)究竟是如何傳參的。傳參的順序和變量創(chuàng)建的順序恰好相反,先傳b再傳a。同時注意函數(shù)傳參并不是在add函數(shù)棧幀內(nèi)完成的,而是在main函數(shù)的棧幀內(nèi)完成,通過寄存器eax和ecx實現(xiàn)變量的傳遞)

?

?【過程九:call? ? 00AD1023】?

?【解析】call指令的作用是:將下一條的指令的ip壓入棧中,并轉(zhuǎn)移到即將被調(diào)用的子程序。相當于push ip +? jmp near ptr 標號。我們現(xiàn)在來觀察棧頂?shù)淖兓?/p>

??在call指令一行們按F11觀察call的作用

?

我們驚奇的發(fā)現(xiàn)棧頂自動壓入了一個元素,元素的值為——call指令的下一條指令的地址?。那壓入這個元素有什么用呢?試想,當call指令調(diào)用add函數(shù)后,我們跳轉(zhuǎn)到add函數(shù)內(nèi)部,那函數(shù)結(jié)束后如何保證我們從add函數(shù)后面的語句繼續(xù)執(zhí)行呢?靠的就是棧頂壓入的地址,根據(jù)這個地址我們可以回到call指令的下一條語句。

?

再次按下F11后我們就跳轉(zhuǎn)到add函數(shù)內(nèi)部。


?五、add函數(shù)棧幀的創(chuàng)建

?

(完整連續(xù)反匯編代碼請看文章結(jié)尾。這里分塊講解)??

?這一部分的操作和main函數(shù)內(nèi)完全一致,都是為棧幀的創(chuàng)建做準備。畫一個動圖,不再贅述。

?

?我們現(xiàn)在研究接下來指令。?

?

(完整連續(xù)反匯編代碼請看文章結(jié)尾。這里分塊講解)??

?【過程一:mov? ? dword ptr [ebp-8],0】

?【解析】將ebp - 8 的地址賦值為0。也就代表著將Z初始化為0

?

?

【過程二:mov? ? eax, dword ptr [ebp+8]】

?【解析】將 ebp + 8 (從上圖觀察)處的值賦值給eax。此時eax儲存著形參a的值

【過程三:add ? ? eax, dword ptr [ebp+0Ch]】

【解析】將eax加上 ebp + 12 處的值。此時eax表示這a+b的值

【過程四:mov? ?? dword ptr [ebp-8], eax】

【解析】將eax儲存的a+b的值傳送到ebp - 8的地址處,也就是賦值給變量Z

【總結(jié)】從上面我們也可以加深這樣的認識:形參只是實參的一個臨時拷貝,所以修改形參當然不會影響實參。?

?

?

【過程五:mov? ? ?eax, dword ptr [ebp-8]】

【解析】我們知道函數(shù)內(nèi)創(chuàng)建的臨時變量出函數(shù)后被銷毀,那返回值是如何被帶回主函數(shù)的呢?靠的就是寄存器eax。這一步的操作就是 return返回 值的過程。

?

(完整連續(xù)反匯編代碼請看文章結(jié)尾。這里分塊講解)??


?六、add函數(shù)棧幀的銷毀?

【過程一:pop ? ?edi / esi / ebx】

【解析】pop的作用是將棧頂?shù)臄?shù)據(jù)彈出,彈出數(shù)據(jù)儲存到相應寄存器中。每次pop過程中esp的地址自動加4字節(jié)。

?

【過程二:mov? ? esp, ebp?】

?【解析】一句話就回收了為add函數(shù)開辟的內(nèi)存

?

【過程三:pop ebp】

【解析】彈出棧頂?shù)脑?,并將彈出的?shù)據(jù)儲存到ebp寄存器中。由于此時的棧頂元素事先存入main函數(shù)中ebp的地址,所以pop ebp時,將main函數(shù)中的ebp元素的地址存入ebp寄存器中

?

?【過程四:ret】

?【解析】ret指令的作用實際相當于 pop IP。在這里實際就是彈出了棧頂事先存儲的add下一條指令的地址,并跳轉(zhuǎn)到該地址處。

?

?

?

(完整連續(xù)反匯編代碼請看文章結(jié)尾。這里分塊講解)??

?【過程五:mov ? ? dword ptr [ebp-20h],eax】

?

?【解析】監(jiān)視我們可以發(fā)現(xiàn)ebp - 20所指向的對象就是c,所以這條語句的作用就是將返回值存儲到c中

?


七、main函數(shù)棧幀的銷毀

?

(完整連續(xù)反匯編代碼請看文章結(jié)尾。這里分塊講解)??

?如何理解之后的語句呢,其實和add函數(shù)棧幀的銷毀基本是一致的,因為我們前面提到,main函數(shù)也是被其他函數(shù)調(diào)用的。以此類推。


八、完整反匯編代碼

int main(){00031410  push        ebp  00031411  mov         ebp,esp  00031413  sub         esp,0E4h  00031419  push        ebx  0003141A  push        esi  0003141B  push        edi  0003141C  lea         edi,[ebp-0E4h]  00031422  mov         ecx,39h  00031427  mov         eax,0CCCCCCCCh  0003142C  rep stos    dword ptr es:[edi]  	int a = 10;0003142E  mov         dword ptr [a],0Ah  	int b = 20;00031435  mov         dword ptr [b],14h  	int c = add(a,b);0003143C  mov         eax,dword ptr [b]  0003143F  push        eax  00031440  mov         ecx,dword ptr [a]  00031443  push        ecx  00031444  call        _add (0310E6h)  00031449  add         esp,8  0003144C  mov         dword ptr [c],eax  	return 0;0003144F  xor         eax,eax  }00031451  pop         edi  00031452  pop         esi  00031453  pop         ebx  00031454  add         esp,0E4h  0003145A  cmp         ebp,esp  0003145C  call        __RTC_CheckEsp (03113Bh)  00031461  mov         esp,ebp  00031463  pop         ebp  00031464  ret  
int add(int x, int y){000313C0  push        ebp  000313C1  mov         ebp,esp  000313C3  sub         esp,0CCh  000313C9  push        ebx  000313CA  push        esi  000313CB  push        edi  000313CC  lea         edi,[ebp-0CCh]  000313D2  mov         ecx,33h  000313D7  mov         eax,0CCCCCCCCh  000313DC  rep stos    dword ptr es:[edi]  	int z = 0;000313DE  mov         dword ptr [z],0  	z = x + y;000313E5  mov         eax,dword ptr [x]  000313E8  add         eax,dword ptr [y]  000313EB  mov         dword ptr [z],eax  	return z;000313EE  mov         eax,dword ptr [z]  }000313F1  pop         edi  000313F2  pop         esi  000313F3  pop         ebx  000313F4  mov         esp,ebp  000313F6  pop         ebp  000313F7  ret  

?【注】這是后面補充的便于大家對知識的掌握有一個連貫性,所以在地址上有些不同。關(guān)注重要的指令即可。


九、后記

? ? ? ? 恭喜你學完了函數(shù)棧幀的創(chuàng)建和銷毀!覺得不錯可以點個贊。大家最近是不是有一大波考試呢?祝大家考的都會,蒙的全對。

?

文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/125080.html

相關(guān)文章

  • C語言深層理解:函數(shù)幀的創(chuàng)建銷毀

    摘要:語言深層理解函數(shù)中棧幀的創(chuàng)建與銷毀引言引言問題一引言問題二引言問題三一棧的簡單認識內(nèi)存的簡單了解棧的簡單了解棧的定義棧的結(jié)構(gòu)二寄存器與簡單的匯編指令寄存器的定義寄存器的分類簡單的匯編指令三棧幀的創(chuàng)建于銷毀調(diào)試調(diào)用堆棧調(diào) ...

    archieyang 評論0 收藏0
  • C語言學習筆記—P17(函數(shù)幀的創(chuàng)建銷毀<超詳解版>+圖解+題例)

    摘要:目錄前言由于作者水平有限,文章難免存在謬誤之處,敬請讀者斧正,俚語成篇,懇望指教前言由于作者水平有限,文章難免存在謬誤之處,敬請讀者斧正,俚語成篇,懇望指教作者新曉故知作者新曉故知那些代碼背后的故事那些代碼背后的故事通過 目錄 前言:●由于作者水平有限,文章難免存在謬誤之處,敬請讀者斧正,俚...

    gnehc 評論0 收藏0
  • 函數(shù)棧幀解析

    摘要:函數(shù)棧幀的銷毀匯編語言了解函數(shù)傳參函數(shù)返回值如何返回函數(shù)中變量如何初始化和賦值函數(shù)執(zhí)行結(jié)束后系統(tǒng)進行了什么操作 文章目錄 一、什么是函數(shù)棧幀 1.寄存器2.函數(shù)棧幀3.棧幀的作用和維護4.棧幀結(jié)構(gòu)二、函數(shù)棧幀的創(chuàng)建? 1.匯編2.main函數(shù)3.Add函數(shù)的創(chuàng)建三、函數(shù)...

    MonoLog 評論0 收藏0
  • 理解函數(shù)棧幀

    摘要:同時,和所指示的位置會隨著函數(shù)棧幀的創(chuàng)建和銷毀而不斷的發(fā)生改變。再次執(zhí)行函數(shù)棧幀的創(chuàng)建操作。函數(shù)的返回值會存在一個寄存器中當函數(shù)棧幀釋放后,返回值不會隨之消失。二函數(shù)棧幀的銷毀將一些函數(shù)調(diào)用中使用的寄存器彈出棧。 ...

    番茄西紅柿 評論0 收藏0
  • C語言】從入門到入土(指針篇)

    摘要:在位機器上,如果有個地址線,那一個指針變量的大小是個字節(jié),才能存放一個地址。就是一個指針變量,也有自己的類型,指針變量的類型我們可以發(fā)現(xiàn)指針的定義方式是類型星號。也就是說存儲什么變量類型就用什么指針變量類型。 ...

    陳偉 評論0 收藏0

發(fā)表評論

0條評論

最新活動
閱讀需要支付1元查看
<