堆和棧的區別 3

2021-05-23 15:53:54 字數 2231 閱讀 2675

├———————┤低端記憶體區域 

│ …… │ 

├———————┤ 

│ 動態資料區 │ 

├———————┤ 

│ …… │ 

├———————┤ 

│ **區 │ 

├———————┤ 

│ 靜態資料區 │ 

├———————┤ 

│ …… │ 

├———————┤高階記憶體區域 

堆疊是乙個先進後出的資料結構,棧頂位址總是小於等於棧的基位址。我們可以先了解一下函式呼叫的過程,以便對堆疊在程式中的作用有更深入的了解。不同的語言有不同的函式呼叫規定,這些因素有引數的壓入規則和堆疊的平衡。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回 收 注意它...