日本免费高清视频-国产福利视频导航-黄色在线播放国产-天天操天天操天天操天天操|www.shdianci.com

學無先后,達者為師

網站首頁 編程語言 正文

C語言函數棧幀的創建與銷毀詳解_C 語言

作者:tjh94512 ? 更新時間: 2022-04-25 編程語言

前言

大家在學習的時候一定有以下困惑: 局部變量是怎么創建的?為什么局部變量的值是隨機值?函數是怎么傳參?傳參的順序是怎樣的?形參與實參是什么關系?函數調用是怎么做到的?函數調用完成不是銷毀了嗎,如何帶回的返回值?

以上這些都可以通過了解函數棧幀的創建與銷毀來理解。接下來我就帶大家來了解函數棧幀的創建與銷毀。

本次使用的編輯器是VS2013,因為越先進的編輯器,越不容易觀察到底層。好了,我們回到正題。

一、函數棧幀是什么?

1.寄存器

寄存器里有eax,ebx,ecx,edx,ebp,esp等,我們今天要重點了解的是ebp與esp這兩個寄存器。

2.ebp與esp

這兩個寄存器中存放的是地址,整兩個地址使用來維護函數棧幀的。每一個函數調用,都要在棧區創建一個空間,這塊空間叫做函數棧幀,用esp與ebp來維護。

二、函數棧幀的創建

1.代碼塊

#include
int Add(int x, int y)
{
	int z = 0;
	z = x + y;
	return z;
}
int main()
{
	int a = 10;
	int b = 20;
	int ret = Add(a, b);
	printf("ret=%d\n", ret);
	return 0;
}

這個代碼只是一個很簡單的實現了加法函數,不用多說。

2.調用堆棧

接下來開始調試程序來看底層代碼如何實現,首先打開調用堆棧

在這里插入圖片描述

從這里可以清楚地知道,main函數之前還有兩個函數在調用,所以大家平時認為的main函數就是最初的開始是錯誤的哦。

3.esp與ebp如何維護棧幀

在棧的使用中,是從高地址開始的。其中ebp指向棧底,esp指向棧頂。

在這里插入圖片描述

main函數被調用后,程序會為main函數分配棧空間。接下來調試并右擊鼠標轉到反匯編。可以看到

在這里插入圖片描述

這些代碼的意思是

將ebp的值壓棧,然后將esp賦給ebp,給esp減去0E4h的大小,之后分別將ebx,esi,edi壓棧

壓棧的意思是將元素放到棧頂。在上面調用堆棧可以看到main函數由__tmainCRTStartup調用。圖解如下:

在這里插入圖片描述

值得一提的是,在棧頂放元素是esp會自動指向新放元素的上方。

在這里插入圖片描述

在這里插入圖片描述


上圖中我們很容易的看出在調用main函數是為main函數開辟的棧空間即棧幀,并且將esp的值給ebp,ebp和esp指向同一塊空間,然后esp變小指向上面的區域,接下來將ebx,esi,edi壓棧。

在這里插入圖片描述

在這里插入圖片描述

接下來這四句,lea是加載有效地址,從上圖中,我們知道ebp指向的地址,那么edi存放的就是ebp-0E4h的地址也就是③esp處的地址,最后一句rep stos dword ptr es:[edi] 意思是從edi里面重復拷貝ecx次eax的內容。一次拷貝四個字節,dword是四個字節的意思。

在這里插入圖片描述

接下來這三個匯編代碼的意思就是將值放入相應的空間。至于為什么是為什么是ebp-8和ebp-20,這個和編譯器有關,就不過多敘述。

在這里插入圖片描述

在這里插入圖片描述

由匯編代碼可知,接下來就開始創建形式參數,將要傳遞的參數值存入eax與ecx這兩個寄存器中并壓入棧頂,所以創建的形式參數并不在add函數的函數棧幀哦,并且我們之前常說的形參是實參的一份臨時拷貝無疑是非常正確的。

在這里插入圖片描述

在這里插入圖片描述

接下來開始add函數調用。call是調用的意思,后面是add函數的地址用來找到調用函數。這里值得一提的是call指令下一條指令的地址被存儲來方便執行完add函數可以跳回來。

Add函數創建棧幀的過程其實和main函數一樣的,先將ebp壓棧方便找到指向main函數棧底的空間,再將esp的地址存放到ebp里面,此時,esp和ebp指向同一位置,再將esp上移0CCh個位置,然后就是ebx,esi,edi壓棧,這里不多作說明,由下圖可清晰理解:

在這里插入圖片描述

在這里插入圖片描述

接下來初始化z與執行加法也沒什么要說的,z=x=y是找到之前創建的形參來進行加法。并將結果移動到z里。

在這里插入圖片描述

返回z的操作是將z的值存放到eax這個寄存器中,所以函數銷毀變量與返回的值這個操作并不沖突。

在這里插入圖片描述

最后銷毀add函數棧幀,pop就是彈出元素然后把元素放入后面的寄存器,每次pop都會銷毀一個棧頂元素然后esp自動++往下挪移。三下pop之后要回收空間了,操作是把ebp的值傳給esp這樣esp指向的空間是main函數的棧頂,然后popebp這個操作會將之前棧底存放的之前存放的main函數的棧底指針傳給ebp,這樣ebp就指向了main函數的棧底。如下圖:

在這里插入圖片描述

接下來銷毀main函數的操作也與之前一樣,就不細說了。

總結?

原文鏈接:https://blog.csdn.net/tjh94512/article/details/123030598

欄目分類
最近更新