學習本篇文章之前,你或許還有這些疑問:
區域變數是怎么創建的?
為什么區域變數的值是隨機值?
函式是怎么傳參的?傳參的順序是什么?
形參和實參是什么關系?
函式呼叫結束后怎么回傳?
看完這篇文章,一切將豁然開朗……
預備知識
在進入正題之前,先來談談相關的概念,有助于之后更好地理解函式堆疊幀
暫存器
暫存器是CPU內部用來存放資料的一些小型存盤區域,用來暫時存放參與運算的資料和運算結果,如eax、ebx、ecx、edx……在觀察函式堆疊幀時,有兩個暫存器非常關鍵,分別是ebp暫存器與esp暫存器,ebp通常指向堆疊底,存盤堆疊底地址,因此又叫堆疊底暫存器;esp通常指向堆疊頂,存盤堆疊頂地址,因此又叫堆疊頂暫存器,函式堆疊幀就是由這兩個暫存器來維護的,
main()函式的執行順序
我們常說main函式是程式的入口,main函式似乎就是最先執行的函式,但如果我們查看底層匯編語言,不難發現main函式居然也是被呼叫的!
在vs2013的環境下,運行程式時,首先由mainCRTStartup呼叫_tmainCRTStrtup,之后再由_tmainCRTStrtup呼叫main()函式,
堆疊區中,空間開辟的準則
每個函式的呼叫,都要在堆疊區開辟一個空間,而這個空間的開辟,會按照高地址向低地址的方向執行,也就是說在呼叫函式時,首先會在堆疊區的高地址處開辟空間,
每次開辟空間,在堆疊頂上方元素的程序叫壓堆疊(push);從堆疊頂洗掉元素的程序叫做出堆疊(pop),
函式堆疊幀的創建與銷毀
了解了以上知識,我們就可以談一談函式堆疊幀創建與銷毀的程序了,
首先我們需要一個簡單的代碼,幫助我們查看它的實作邏輯:
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
int Add(int x, int y)
{
int z = 0;
z = x + y;
return z;
}
int main()
{
int a = 10;
int b = 20;
int c = 0;
c = Add(a, b);
printf("%d\n", c);
return 0;
}
程式開始執行,首先ebp指向_tmainCRTStrtup的高地址,esp指向_tmainCRTStrtup的低地址,此時他們共同維護_tmainCRTStrtup的空間,

接著,_tmainCRTStrtup呼叫mian函式,首先將ebp的值壓堆疊放入,之后esp指向堆疊頂,

隨后esp的值將賦給sbp;esp再減去0E4h,即指向的地址向上跑,經此操作過后,ebp與esp有了新的維護空間,即main的空間,

之后又是壓堆疊的程序,將ebx、esi、edi先后壓入,這里我們不用關注具體壓入的值 ,

這些操作完成以后,edi會存入main函式堆疊頂的地址,在之后會產生重要作用,這一步完成后,main函式空間的值將會被完全改變,變成cccccccc……經過這些,main函式的空間才算開辟完成,

看看我們的代碼,接下來就要執行變數的定義與初始化操作了,
首先會將10的值放入epb-8中,即對于int a = 10;之后以同樣的方式存入b與c,值得關注的是,vs環境下每個變數間隔著2個位元組的空間,不同的編譯環境下間隔空間不一定相同!
可以看到,如果我們不初始化變數a,a的空間將是cccccccc,也就是我們經常見到的亂數,

程式繼續執行,將進入Add函式,首先進行的是傳參操作,
先將b的值存入eax中壓堆疊,再將a的值存入ecx中壓堆疊,這樣一來,就完成了傳參的操作,之后會壓入下一條指令的地址,便于空間銷毀時步驟的回傳,

接下來的操作與為main函式開辟空間類似,首先壓入ebp的值,此時的ebp指向main函式的堆疊底,
接下來省略部分重復的操作,直到為Add函式創建好堆疊幀,
至此,開始執行Add函式,首先完成z的定義與初始化,與之前一樣,z的空間將選擇在ebp-8處,
之后進行計算,計算順序如下:
ebp+8的值(10)放入eax中——>將epb+12的值(20)放入eax中——>將eax放入epb-8(即z)中
此時z中的值就成了我們想要的30了,

函式執行完成,就要進行z的回傳了,在這一步驟中,會將ebp-8的值(30)放入暫存器eax中,接著執行彈出操作,
esp與ebp將將一步一步退回,,ebp將根據之前存盤的main函式的地址回到main函式的堆疊底,而esp會找到之前壓入的地址,

通過這一地址,程式繼續執行,同時形參空間銷毀,暫存器eax將回傳值放入c中,完成了回傳程序,

至此,函式堆疊幀創建與銷毀的程序已經全部描述完整,
總結
相信看到這,之前所具有的疑問均已經恍然大悟,
由于為函式開辟空間后會自動存滿ccccc……因此,如果不對區域變數進行賦值,它所指向的空間也充滿cccccccc,即為隨機值,
函式在傳參時,會先傳右邊的,再傳左邊的,可以說形參是實參的一份臨時拷貝,
函式呼叫結束后的回傳值會通過存入暫存器的方式供之后使用,
轉載請註明出處,本文鏈接:https://www.uj5u.com/ruanti/292538.html
標籤:其他
上一篇:爬蟲的實戰應用
下一篇:面向物件編程
