1. 程式人生 > >Linux基礎篇九——函式的棧幀

Linux基礎篇九——函式的棧幀

一、棧幀的概念

什麼是棧幀,度娘給出瞭如下的解答:

棧幀也叫過程活動記錄,是編譯器用來實現過程/函式呼叫的一種資料結構

我們在呼叫函式時,總是要和這位叫做棧幀的朋友打交道,那麼,你有沒有試圖去了解過他呢?

二、棧幀的結構

在博主的另外一篇部落格裡曾經提到過程序的地址空間,我們現在再來看一下什麼是程式的地址空間

在上圖裡,我們發現了一個我們很熟悉的資料結構叫做棧。(棧向下生長)

       棧幀,用我們容易理解的話來說,是機器用棧來傳遞過程引數,儲存返回資訊,儲存暫存器用於以後恢復,以及本地儲存的一種結構

       為單個函式呼叫分配扥那部分棧我們稱之為棧幀

       讓我們具體來看看一個執行時堆疊的結構吧


我們知道,棧的生長是從高地址到低地址的,每次在我們呼叫一個函式時實際上發生瞭如下的過程:

1.  將返回地址壓棧(返回之後要執行的下一條指令地址),將呼叫者的ebp(基址)壓棧

2.  讓ebp指向現在esp的位置

3.  開闢新的空間以供被呼叫的函式使用

函式在呼叫時一定會儲存其返回地址,這樣在該函式結束時我們才能正確的找到我們需要執行的下一條語句以保證正確的程式執行流。

暫存器ebp為幀指標,而暫存器esp為棧指標,當程式執行時,棧指標可以移動,而大多數資訊的訪問時通過幀指標獲取

棧幀的起始位置應該是從儲存幀指標的位置開始計算,棧幀的末尾位置則是儲存返回地址處。

***我們所傳的函式的引數實際上儲存在呼叫該函式的函式的棧幀中。例如Q呼叫了P那麼P的引數實際儲存在Q的棧幀中。

既然我們知道了棧幀的結構 ,我們就可以利用棧幀的結構特點來做一些有趣的事,就比如,我們可以不使用變數名而改變變數的值

程式如圖:


該程式的結果如下圖


我們發現莫名其妙地a的值就被默默地修改了,我們首先來看一下具體的程式的意思,我們在main函式中定義了兩個變數,a,b,他們都在main函式的棧幀中,此時我用一個指標指向b的起始地址向高地址偏移了一整個陣列的位置(陣列的壓棧順序是從高地址到低地址處下標也是從高到低的),然後將其作為int*來解析的,這樣我們就可以成功的找到我們的變數a並修改它的值。

實際上這是一種十分不安全的做法,並不推薦因為覺得有趣而常常這樣寫程式。