乙個c/c++編譯的程式占用的記憶體分為以下幾個部分:1.棧區(stack):由編譯器自動分配和釋放,存放函式的引數值,區域性變數的值,返回資料,返回位址等。操作方式類似於資料結構中的棧。
2.堆區(heap):一般由程式設計師分配和釋放,若程式設計師不釋放,程式結束時可能由作業系統**。與資料結構中的堆是兩碼事,分配方式類似於鍊錶。
3.全域性區(靜態區)(static):存放全域性變數、靜態資料、常量。程式結束後由系統釋放。
4.文字常量區:常量字串放在此,程式結束後由系統釋放。
5.程式**區:存放函式體(類成員函式和全域性函式)的二進位制**。
記憶體區域分配如圖所示:
其中堆和棧相對而生。
堆和棧的申請方式:
棧由系統自動分配,速度較快,在windows下棧是向低位址擴充套件的資料結構,是一塊連續的記憶體區域,大小是2mb。
堆需要程式設計師自己申請,並指明大小,速度比較慢。在c中用malloc申請,c++中用new申請。另外,堆是向高位址擴充套件的資料結構,是不連續的記憶體區域,堆的大小受限於計算機的虛擬記憶體。因此堆空間獲取和使用比較靈活,可用空間較大。
接下來由乙個例項具體分析呼叫過程
#include
#include
int add(int
x, int
y)int main()
在具體分析之前先了解3個重要暫存器:main函式組合語言如下:ebp:基址暫存器,存放指向函式棧幀棧底的位址。
esp:棧頂暫存器,存放指向函式棧幀棧頂的位址。
eip/ip/pc:程式計數器,儲存的內容指向下一條指令。
int main()
add函式組合語言如下:
pop edi //出棧
00e91402
pop esi //出棧
00e91403
pop ebx //出棧,使esp向下移動
00e91404
mov esp,ebp //將ebp賦值給esp
00e91406
pop ebp
00e91407
ret //ret指令會使得出棧一次,並將出棧的內容當作位址,將程式執行跳轉到該位址處。
//此位址將被壓入棧中
由此分析,x,y是連續存放的,所以我們可以通過修改x的位址進而修改y的值
如:
#include
#include
int add(int
x, int
y)int main()
又如:#include
#include
void bug()
int add(int
x, int
y)int main()
#include
#include
void *c = null;
void bug()
int add(int
x, int
y)int main()
程式還是會崩潰,因為呼叫add函式時使用的是call彙編語句,而呼叫bug函式是直接修改其ip位址。但兩個函式最後都有乙個ret結構,將棧頂的返回值彈出值eip中,所以經歷了兩次出棧,卻只有一次壓棧。需要平衡棧幀結構。
改進如下:
#include
#include
void *c = null;
void bug()
int add(int
x, int
y)int main()
system("pause");
return
0;}
棧幀與函式呼叫過程
程式的位址空間圖中,從下向上,位址不斷增長,從0x00000000到0xffffffff。code 區,儲存 字元 串常量區 儲存不能被修改的常量 init,uninit 全域性區,分別為初始化全域性區,和未初始化全域性區,儲存全域性變數 heap 堆,使用malloc在堆申請空間,在堆上建立的變數...
函式呼叫過程及棧幀分析
linux核心程式boot head.s執行完基本初始化操作之後,就會跳轉去執行init main.c程式。那麼head.s程式是如何把執行控制轉交給init main.c程式的呢?即匯程式設計序是如何呼叫執行c語言程式的?這裡我們首先描述一下c函式的呼叫機制 控制權傳遞方式,然後說明head.s程...
函式呼叫過程(棧幀)
眾所周知,程式每呼叫乙個函式,系統都會為其開闢一塊空間,當它返回時,才收回這塊空間。程式崩潰有一部分原因就是因為無限制的呼叫函式,卻沒有及時返回,導致記憶體空間不夠。為了更好的維護這一塊空間 通常稱為棧空間 我們需要了解兩個暫存器,乙個為 esp 指向棧頂的指標 乙個為 ebp 指向棧底的指標 棧空...