├———————┤低端記憶體區域
│ …… │
├———————┤
│ 動態資料區 │
├———————┤
│ …… │
├———————┤
│ **區 │
├———————┤
│ 靜態資料區 │
├———————┤
│ …… │
├———————┤高階記憶體區域
堆疊是乙個先進後出的資料結構,棧頂位址總是小於等於棧的基位址。我們可以先了解一下函式呼叫的過程,以便對堆疊在程式中的作用有更深入的了解。不同的語言有不同的函式呼叫規定,這些因素有引數的壓入規則和堆疊的平衡。windows api的呼叫規則和ansi c的函式呼叫規則是不一樣的,前者由被調函式調整堆疊,後者由呼叫者調整堆疊。兩者通過「__stdcall」和「__cdecl」字首區分。先看下面這段**:
#include
void __stdcall func(int param1,int param2,int param3)
int main()
編譯後的執行結果是:
0x0012ff78
0x0012ff7c
0x0012ff80
0x0012ff68
0x0012ff6c
0x0012ff70
├———————┤<—函式執行時的棧頂(esp)、低端記憶體區域
│ …… │
├———————┤
│ var 1 │
├———————┤
│ var 2 │
├———————┤
│ var 3 │
├———————┤
│ ret │
├———————┤<—「__cdecl」函式返回後的棧頂(esp)
│ parameter 1 │
├———————┤
│ parameter 2 │
├———————┤
│ parameter 3 │
├———————┤<—「__stdcall」函式返回後的棧頂(esp)
│ …… │
├———————┤<—棧底(基位址 ebp)、高階記憶體區域
上圖就是函式呼叫過程中堆疊的樣子了。首先,三個引數以從又到左的次序壓入堆疊,先壓「param3」,再壓「param2」,最後壓入「param1」;然後壓入函式的返回位址(ret),接著跳轉到函式位址接著執行(這裡要補充一點,介紹unix下的緩衝溢位原理的文章中都提到在壓入ret後,繼續壓入當前ebp,然後用當前esp代替ebp。然而,有一篇介紹windows下函式呼叫的文章中說,在windows下的函式呼叫也有這一步驟,但根據我的實際除錯,並未發現這一步,這還可以從param3和var1之間只有4位元組的間隙這點看出來);第三步,將棧頂(esp)減去乙個數,為本地變數分配記憶體空間,上例中是減去12位元組(esp=esp-3*4,每個int變數占用4個位元組);接著就初始化本地變數的記憶體空間。由於「__stdcall」呼叫由被調函式調整堆疊,所以在函式返回前要恢復堆疊,先**本地變數占用的記憶體(esp=esp+3*4),然後取出返回位址,填入eip暫存器,**先前壓入引數占用的記憶體(esp=esp+3*4),繼續執行呼叫者的**。參見下列彙編**:
;--------------func 函式的彙編**-------------------
:00401000 83ec0c sub esp, 0000000c //建立本地變數的記憶體空間
:00401003 8b442410 mov eax, dword ptr [esp+10]
:00401007 8b4c2414 mov ecx, dword ptr [esp+14]
:0040100b 8b542418 mov edx, dword ptr [esp+18]
:0040100f 89442400 mov dword ptr [esp], eax
:00401013 8d442410 lea eax, dword ptr [esp+10]
:00401017 894c2404 mov dword ptr [esp+04], ecx
……………………(省略若干**)
:00401075 83c43c add esp, 0000003c ;恢復堆疊,**本地變數的記憶體空間
:00401078 c3 ret 000c ;函式返回,恢復引數占用的記憶體空間
堆和棧區別
一 預備知識 程式的記憶體分配 乙個由c c 編譯的程式占用的記憶體分為以下幾個部分 1 棧區 stack 由編譯器自動分配釋放 存放函式的引數值,區域性變數的值等。其 操作方式類似於資料結構中的棧。2 堆區 heap 一般由程式設計師分配釋放,若程式設計師不釋放,程式結束時可能由os回 收 注意它...
堆和棧區別
管理方式 棧由編譯器自動管理 堆由程式設計師控制,使用方便,但易產生記憶體洩露。生長方向 棧向低位址擴充套件 即 向下生長 是連續的記憶體區域 堆向高位址擴充套件 即 向上生長 是不連續的記憶體區域。這是由於系統用鍊錶來儲存空閒記憶體位址,自然不連續,而鍊錶從低位址向高位址遍歷。空間大小 棧頂位址和...
堆和棧區別
一 預備知識 程式的記憶體分配 乙個由c c 編譯的程式占用的記憶體分為以下幾個部分 1 棧區 stack 由編譯器自動分配釋放 存放函式的引數值,區域性變數的值等。其 操作方式類似於資料結構中的棧。2 堆區 heap 一般由程式設計師分配釋放,若程式設計師不釋放,程式結束時可能由os回 收 注意它...