1. 程式人生 > >淺談記憶體分配方式以及堆和棧的區別(很清楚)

淺談記憶體分配方式以及堆和棧的區別(很清楚)

對於一個程式要執行,涉及到的記憶體分配是一個首要問題,這裡簡單說一下一個簡單的程式執行所涉及到的記憶體分配方式。另外,在資料結構中存在堆和棧的概念,棧是一種先進後出的資料結構,堆則是一種排序方式,而在記憶體分配中也存在堆(heap)和棧(stack)的概念,與資料結構中的概念不同,這裡簡單說明在記憶體分配中的堆疊之間的不同。

一、記憶體分配方式

1、全域性變數和靜態變數(static變數),是由編譯器自動分配和釋放的,初始化的全域性變數和靜態變數放在同一塊記憶體區中,未初始化的全域性變數和靜態變數則放在相鄰的另外一塊記憶體區中。

2、棧,是由編譯器自動分配和釋放的,主要是函式體的地址,引數和區域性變數,靜態變數不包含其中,操作方式類似於資料結構中的棧。

3、堆,是由程式設計師手動完成申請和釋放的,像malloc和new,程式設計師沒有手動釋放的話,當程式結束時由系統釋放沒有釋放的空間,其實現方式與資料結構中的堆完全不同,此時的堆的實現方式有些類似於資料結構中的連結串列。

4、程式程式碼區,用於存放程式的二進位制程式碼的空間。

5、文字常量區,像常量字串等存放在這裡,程式結束後由系統釋放。

一個經典的例子,由強大的網友提供:

//main.cpp

int a = 0; 全域性初始化區

char *p1; 全域性未初始化區

main()

{

int b; 棧

char s[] = "abc"; 棧

char *p2; 棧

char *p3 = "123456"; 123456\0在常量區,p3在棧上。

static int c =0; 全域性(靜態)初始化區

p1 = (char *)malloc(10);

p2 = (char *)malloc(20);

分配得來得10和20位元組的區域就在堆區。

strcpy(p1, "123456"); 123456\0放在常量區,編譯器可能會將它與p3所指向的"123456"優化成一個地方。

}

二、記憶體分配中堆和棧的區別

1.申請釋放方式

   在申請記憶體和釋放記憶體方式方面,堆和棧有著很大的不同,先來談談棧是如何實現的吧。棧是編譯器自動申請的,例如在主函式裡面,要宣告一個int變數a,那麼編譯器就自動開闢一塊記憶體存放變數a。而堆則不相同,是由程式設計師手動申請的,只要程式設計師感覺程式此處需要用到多大的記憶體空間,那麼就使用malloc或者new來申請固定大小的記憶體使用。棧的空間在程式結束的時候由系統或者編譯器自動釋放,而堆則在程式結束前由程式設計師手動使用delete釋放,或者忘記手動釋放,由系統在程式結束的時候自動回收。

2.申請後系統的相應

棧,只要棧剩餘的空間大小比申請的空間小,系統就自動為其分配空間,否則就會報錯說明棧空間溢位。

堆,首先要知道作業系統中有一個存放空閒儲存塊的連結串列,當程式設計師申請空間的時候,系統就會遍歷整個連結串列,找到第一個比申請空間大的空閒塊節點,系統會將該空閒塊從空閒連結串列中刪除,分配給程式,同時系統會記錄這個空閒塊的首地址和申請的大小,當程式設計師使用delete釋放該空間的時候能夠找到該儲存區。另外,申請的空間不一定與找到的空閒塊大小相同,多出來剩餘的空閒區會被系統重新新增到空閒連結串列中。

3.申請的限制

棧,是一種向低地址擴充套件的資料結構,並且是連續的儲存空間,所以棧頂和棧的最大容量是固定的,在windows下,棧的最大容量是2m或者是1m,是在編譯的時候就已經確定的,當申請空間大於棧的剩餘空間的時候,就會報錯說明overflow,所以棧能夠申請的空間是比較有限的。

堆,是一種向高地址擴充套件的資料結構,並且是不連續的,因為系統採用的是連結串列的方式存放空閒儲存塊,當然是不連續的,連結串列的遍歷方向是由低向高的,所以堆能夠申請的空間的大小其實等同於整個系統的虛擬記憶體,只要還有記憶體空間,那麼堆就能夠不受限制的申請空間,這種方式比較靈活,申請空間也較大。

4.申請效率的比較

棧,因為棧空間的申請是由系統自動完成的,所以速度快,但是不受程式設計師控制。

堆,空間的申請是由malloc或new來完成的,實現起來較慢,能夠產生碎片,但是使用起來方便。

5.存放內容

棧,棧存放的內容,一般來說是函式地址和相關引數。當主函式要呼叫一個函式的時候,要對當前斷點進行儲存,需要使用棧來實現,首先入棧的是主函式下一條語句的地址,然後是呼叫函式的引數,一般情況下是按照從右向左的順序入棧,之後是呼叫函式的區域性變數,注意靜態變數是存放在全域性記憶體區,是不入棧的;出棧的順序正好相反,最終棧頂指向主函式下一條語句的地址,主程式又從該地址開始執行。

堆,一般情況堆頂使用一個位元組的空間來存放堆的大小,而堆中具體存放內容是由程式設計師來完成的。

參考資料:http://zhidao.baidu.com/question/6117772.html

補充:

全域性變數、區域性變數、全域性靜態變數、區域性靜態變數的區別。要從分配記憶體的位置和作用域入手來解釋。

全域性變數,分配的記憶體在靜態儲存區記憶體上面,其作用域是全域性作用域,也就是整個程式的生命週期內都可以使用,同時,有些程式並不是由一個原始檔構成的,可能有許多個原始檔構成,全域性變數只要在一個檔案中定義,就可以在其他所有的檔案中使用,當然,必須在其他檔案使用extern關鍵字宣告該變數。

區域性變數,分配記憶體是分配在棧儲存區上的,其作用域也只是在區域性函式內,在定義該變數的函式內,只要出了該函式,該區域性變數就不再起作用,該變數的生命週期也只是和該函式同在。

全域性靜態變數,分配的記憶體與全域性變數一樣,也是在靜態儲存記憶體上,其生命週期也是與整個程式同在的,從程式開始到結束一直起作用,但是與全域性變數不同的是,全域性靜態變數作用域只在定義它的一個原始檔內,其他原始檔不能使用它。

區域性靜態變數,分配的記憶體也是在靜態儲存記憶體上的,其第一次初始化後就一直存在直到程式結束,該變數的特點是其作用域只在定義它的函式內可見,出了該函式就不可見了。