C語言超詳細解析函數棧幀_第1頁
C語言超詳細解析函數棧幀_第2頁
C語言超詳細解析函數棧幀_第3頁
C語言超詳細解析函數棧幀_第4頁
C語言超詳細解析函數棧幀_第5頁
已閱讀5頁,還剩10頁未讀 繼續免費閱讀

下載本文檔

版權說明:本文檔由用戶提供并上傳,收益歸屬內容提供方,若內容存在侵權,請進行舉報或認領

文檔簡介

第C語言超詳細解析函數棧幀目錄一、前面二、預備知識三、棧幀創建與銷毀四、總結

一、前面

本章將以匯編視角看函數棧幀的內存是如何使用與回收的,為了降低匯編語言的理解成本,以圖示的方式講解每一步匯編指令所帶來的效果,來逐步展示函數棧幀的形成與銷毀的整個過程。

展示環境:win10vs2025

二、預備知識

這些預備知識理解與否對本篇文章并無很大關系,之所以預備這些知識是為了讓讀者能夠更加相信函數棧幀的形成與銷毀過程就是如此。

棧區:內存四區之一,內存為了使用和管理,被劃分為四部分,其中棧區就是內存被劃分的區域之一,棧的使用習慣是,先使用高地址部分,在使用底地址部分。

函數棧幀:即在調用函數時,為函數開辟的一塊內存空間,由于該內存空間在棧區,因此該空間被稱作函數棧幀,簡稱棧幀。

棧頂:故名思意,就是棧的頂部,更確切的說是指向存放在棧區數據的頂部。

棧底:棧的底部。

寄存器:寄存器cpu內部用來存放數據的一些小型存儲區域,用來暫時存放參與運算的數據和運算結果。簡單來說就是獨立于內存,用來存儲少量數據的器件。

ebp:棧底指針寄存器

esp:棧底指針寄存器

其它寄存器:ebx、esi、edi、ecx、eax

入棧(壓棧):先將棧頂指針向上移動四字節的大小空間,再將寄存器的數據放入那四字節空間。這里的向上移動是指向低地址處移動。

入棧指令:pusha。

圖解:以pusha為例。

出棧:將棧頂指針向下移動四字節,這里的向下是往低地址處移動四個字節的空間。并將這四個字節的數據放入某個寄存器中。

出棧指令:popa。

圖解:以popa為例。

簡單匯編操作指令

movab:將b賦值給a,c語言表示就是a=b。

subab:將a-b的結果賦值給a,c語言表述就是a=a-b。

addab:將a+b的結果賦值給a,c語言表述就是a=a+b。

由于理解成本的原因,遇到的其它匯編指令本文會直接指出它的作用效果。

三、棧幀創建與銷毀

以Add函數調用為例

#define_CRT_SECURE_NO_WARNINGS1

#includestdio.h

intAdd(intx,inty)

intz=x+y;

returnz;

intmain()

inta=10;

intb=20;

intz=0;

z=Add(a,b);

printf("%d\n",z);

return0;

}

該代碼對應的匯編指令如下:

需要說明的是,main函數也被別的函數調用的,調用關系是:__mainCRTStartup調用main函數,mainCRTStartup函數調用__mainCRTStartup。

再調用main函數之前,棧區是這樣的。

指令分說:

intmain()

00F71E40pushebp

00F71E41movebp,esp

00F71E43subesp,0E4h

以上圖為參照。

第一條指令:將寄存器ebp的值壓棧

第二條指令:將寄存器esp的值賦值給ebp

第三條指令:將esp-0E4h賦值給寄存器esp,形象的表述是esp向低地址方向移動4個字節,上端為低地址,下端為高地址,即向上移動4字節空間。

棧區視圖變為:

這三條指令,簡單來說就是為main函數在棧區開辟了一塊空間(這塊空間大小系統會幫我們自動開辟好。)

指令分說:

00F71E49pushebx

00F71E4Apushesi

00F71E4Bpushedi

將三個寄存器的值壓入棧中

棧區視圖變為:

指令分說:

00F71E4Cleaedi,[ebp-24h]

00F71E4Fmovecx,9

00F71E54moveax,0CCCCCCCCh

00F71E59repstosdwordptres:[edi]

這四條指令我們就解讀了,效果就是將main函數的棧幀空間以16進制值cccccccc填充。

棧區視圖變為:

指令分說:

00F71E5Bmovecx,0F7C003h

00F71E60call00F7130C

這兩條指令是編譯器檢查用的,初學不必花費更多時間了解更細節的部分。

vs2013沒有這一檢查部分,vs2025檢查很嚴格。

指令分說:

inta=10;

00F71E65movdwordptr[ebp-8],0Ah

intb=20;

00F71E6Cmovdwordptr[ebp-14h],14h

intz=0;

00F71E73movdwordptr[ebp-20h],0

第一條匯編指令:將0Ah放入[ebp-8]這塊空間中,即把a放入那塊空間。

第二條匯編指令:將14h放入[ebp-14h]這塊空間中,即把b放入那塊空間中。

第三條匯編指令:將0放入[ebp-20h]這塊空間中。即把z放入那塊空間中。

棧區圖示:

簡單來說:就是將局部變量放入對應的函數棧幀中。

指令分說:

z=Add(a,b);

00F71E7Amoveax,dwordptr[ebp-14h]

00F71E7Dpusheax

00F71E7Emovecx,dwordptr[ebp-8]

00F71E81pushecx

第一條指令:將【ebp-20】這塊空間4字節的數據放入eax中。即把b=20的數據放入eax中。

第二條指令:將eax的數據壓棧。

第三條指令:將【ebp-8】這塊空間4字節的數據放入ecx中。即把a=10的數據放入ecx中。

第四條指令:將ecx的數據壓棧。

棧區視圖:

這里的20和10,就是我們傳過去的實參,之后Add函數調用的x和y就是指這兩塊空間。

那么我們可以知道:函數傳參是從右向左傳的。這里就是先傳的b再傳的a。

指令分說:

00F71E82call00F710B4

調用的函數:

intAdd(intx,inty)

{

00F71740pushebp

00F71741movebp,esp

00F71743subesp,0CCh

00F71749pushebx

00F7174Apushesi

00F7174Bpushedi

00F7174Cleaedi,[ebp-0Ch]

00F7174Fmovecx,3

00F71754moveax,0CCCCCCCCh

00F71759repstosdwordptres:[edi]

00F7175Bmovecx,0F7C003h

00F71760call00F7130C

intz=x+y;

00F71765moveax,dwordptr[ebp+8]

00F71768addeax,dwordptr[ebp+0Ch]

00F7176Bmovdwordptr[ebp-8],eax

returnz;

00F7176Emoveax,dwordptr[ebp-8]

}

00F71771popedi

00F71772popesi

00F71773popebx

00F71774addesp,0CCh

00F7177Acmpebp,esp

00F7177Ccall00F71235

00F71781movesp,ebp

00F71783popebp

00F71784ret

第一條匯編指令:call是調用指令,調用Add函數。

經過上次的指令,這里我就直接介紹效果了。

00F71740pushebp

00F71741movebp,esp

00F71743subesp,0CCh

這三條指令,為Add函數在棧區開辟對應的空間大小。

棧區圖示:

00F71749pushebx

00F7174Apushesi

00F7174Bpushedi

將ebx,esi,edi入棧。

圖示:

00F7174Cleaedi,[ebp-0Ch]

00F7174Fmovecx,3

00F71754moveax,0CCCCCCCCh

00F71759repstosdwordptres:[edi]

對Add函數棧幀做初始化,將里面的數據置換為cccccccc。(用于初始化棧幀的具體數值取決于編譯器)

00F7175Bmovecx,0F7C003h

00F71760call00F7130C

編譯器做的檢查,不必理會。

intz=x+y;

00F71765moveax,dwordptr[ebp+8]

00F71768addeax,dwordptr[ebp+0Ch]

00F7176Bmovdwordptr[ebp-8],eax

取[ebp+8]空間的數據放入eax中

取[ebp+0Ch]與eax的數據相加后放入eax中。

將eax的值放入ptr[ebp-8]中。

圖示:

returnz;

00F7176Emoveax,dwordptr[ebp-8]

返回時,通過寄存器的方式,將返回值交給寄存器。

00F71771popedi

00F71772popesi

00F71773popebx

00F71774addesp,0CCh

00F7177Acmpebp,esp

00F7177Ccall00F71235

00F71781movesp,ebp

00F71783popebp

00F71784ret

代碼分說:

00F71771popedi

00F71772popesi

00F71773popebx

將edi、esi、ebx出棧

圖示:

00F71774addesp,0CCh

00F7177Acmpebp,esp

00F7177Ccall00F71235

00F71781movesp,ebp

00F71783popebp

0CCh是Add函數棧幀的大小

所以esp向下移動到dbp的位置。

之后popebp,由于棧頂指向的是main函數棧幀的棧底,因此出棧ebp指向main函數棧幀的棧底。

圖示:

調用Add返回之后,繼續執行以下指令。

00A717F7addesp,8

00A717FAmovdwordptr[ebp-20h],eax

return0;

00A717FDxoreax,eax

}

00A717FFpopedi

00A71800popesi

00A71801popebx

00A71802addesp,0E4h

00A71808cmpebp,esp

00A7180Acall00A71235

00A7180Fmovesp,ebp

00A71811popebp

00A71812ret

第一條指令:將esp向下移動8個字節,即銷毀x和y這兩塊連續的形參。

第二條指令:將寄存器eax保存的Add函數的返回值交給z。

圖示:

之后的指令就是回收main函數的棧幀了,回收過程都差不多,就不細細講解了。

四、總結

以下函數調用為例。

#define_CRT_SECURE_NO_WARNINGS1

#includestdio.h

intAdd(intx,inty)

intz=x+y;

returnz;

intmain()

inta=10;

intb=20;

intz=0;

z=Add(a,b);

return0;

}

初始:mainCRTStartup函數調用__mainCRTStartup、__mainCRTStartup調用main函數。

棧區上先為以上兩個函數分配函數棧幀。

調用main函數時,為main函數分配函數棧幀(該大小是自動開辟

溫馨提示

  • 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
  • 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯系上傳者。文件的所有權益歸上傳用戶所有。
  • 3. 本站RAR壓縮包中若帶圖紙,網頁內容里面會有圖紙預覽,若沒有圖紙預覽就沒有圖紙。
  • 4. 未經權益所有人同意不得將文件中的內容挪作商業或盈利用途。
  • 5. 人人文庫網僅提供信息存儲空間,僅對用戶上傳內容的表現方式做保護處理,對用戶上傳分享的文檔內容本身不做任何修改或編輯,并不能對任何下載內容負責。
  • 6. 下載文件中如有侵權或不適當內容,請與我們聯系,我們立即糾正。
  • 7. 本站不保證下載資源的準確性、安全性和完整性, 同時也不承擔用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。

評論

0/150

提交評論