C語言函數棧幀的創建與銷毀原理圖解

什麼是函數棧幀

我們在寫C語言代碼的時候,經常會把一個獨立的功能抽象為函數,所以C程序是以函數為基本單位的。

那函數是如何調用的?函數的返回值又是如何待會的?函數參數是如何傳遞的?這些問題都和函數棧幀有關系。

函數棧幀(stack frame)就是函數調用過程中在程序的調用棧(call stack)所開辟的空間,這些空間是用來存放:

  • 函數參數和函數返回值
  • 臨時變量(包括函數的非靜態的局部變量以及編譯器自動生產的其他臨時變量)
  • 保存上下文信息(包括在函數調用前後需要保持不變的寄存器)。

什麼是棧?

棧(stack)是現代計算機程序裡最為重要的概念之一,幾乎每一個程序都使用瞭棧,沒有棧就沒有函 數,沒有局部變量,也就沒有我們如今看到的所有的計算機語言。

與函數棧幀有關的匯編語句

eax:通用寄存器,保留臨時數據,常用於返回值

ebx:通用寄存器,保留臨時數據

ebp:棧底寄存器

esp:棧頂寄存器

eip:指令寄存器,保存當前指令的下一條指令的地址

mov:數據轉移指令

push:數據入棧,同時esp棧頂寄存器也要發生改變

pop:數據彈出至指定位置,同時esp棧頂寄存器也要發生改變

sub:減法命令

add:加法命令

call:函數調用,1. 壓入返回地址 2. 轉入目標函數

jump:通過修改eip,轉入目標函數,進行調用

ret:恢復返回地址,壓入eip,類似pop eip命令(返回子程序)

函數如何創建棧幀並銷毀

當程序進入main函數時,要給main函數在棧區創建空間,esp(棧頂)和ebp(棧底)對main函數進行維護

當程序執行時,我們在調試窗口對堆棧段進行調用,我們可以看到main函數是被__tmainSRTStartup函數所調用,說明main函數是它的內部函數,而__tmainSRTStartup又是被mainCRStartup這個函數調用

梳理一下上面的思路

這些函數在堆棧當中的存儲

main函數棧幀開辟

接下來重新調試,我們轉到反匯編

調用main函數之前,esp和ebp對調用main函數的函數進行維護 ,當棧頂發生改變時,esp會指向新的棧頂

003118B0 push ebp

003118B1 mov ebp,esp

先把ebp入棧,然後把ebp移動到esp的位置

003118B3 sub esp,0E4h

003118B9 push ebx

003118BA push esi

003118BB push edi

之後又把esp往上移動,移動完把ebx,esi,edi壓棧,esp和ebp現在指的這塊空間是為main函數預先開辟好的

把edi-0EFH也就是main函數開始的這裡的地址,放到edi裡面去,然後從這個地址開始賦值39次的雙字節數據,賦值為CCCC

到這裡main函數棧幀開辟完成

接下來就是在main函數的空間裡,創建三個變量,並給賦值

調用Add函數

對函數進行傳參,創建倆個臨時變量,然後壓棧進去

接下來進入call開始調用函數call此時的地址是00C2144B

此時按下F11,我們發現call指令的下一條地址被壓到瞭棧區

把call的下一個地址壓棧,ps:後面會用到這條指令,可先放在這不管

接下來進入Add函數,跟前面main函數一樣,先開辟空間,然後賦值為CCCCCC,再為變量在函數裡創建空間並賦值

接下來執行加法運算,由於剛才已經創建好瞭零時變量,所以把他倆進行相加,加完之後把結果傳過來就行,傳過來之後把這個值放在eax裡面去

返回主函數

按順序出棧,之後把ebp賦值給esp

之後pop,ebp把ebp進行出棧,ebp便回到main函數這裡,ebp此時回到這裡,esp也自然而然的往下指一個,ret指令是返回,然後esp來到瞭call指令的下一條指令

把棧頂指針彈出去,esp自然向下指一條

之後給esp加8即釋放這倆個臨時變量

之後把eax放到ebp-20h,eax是存放剛才加法和的地方

到此這篇關於C語言函數棧幀的創建與銷毀原理圖解的文章就介紹到這瞭,更多相關C語言函數棧幀內容請搜索WalkonNet以前的文章或繼續瀏覽下面的相關文章希望大傢以後多多支持WalkonNet!

推薦閱讀: