天天看點

C語言堆棧入門——堆和棧的差別

資料結構的棧和堆

    首先在資料結構上要知道堆棧,盡管我們這麼稱呼它,但實際上堆棧是兩種資料結構:堆和棧。

    堆和棧都是一種資料項按序排列的資料結構。

棧就像裝資料的桶或箱子

    我們先從大家比較熟悉的棧說起吧,它是一種具有後進先出***質的資料結構,也就是說後存放的先取,先存放的後取。這就如同我們要取出放在箱子裡面底下的東西(放入的比較早的物體),我們首先要移開壓在它上面的物體(放入的比較晚的物體)。

堆像一棵倒過來的樹

而堆就不同了,堆是一種經過排序的樹形資料結構,每個結點都有一個值。通常我們所說的堆的資料結構,是指二叉堆。堆的特點是根結點的值最小(或最大),且根結點的兩個子樹也是一個堆。由于堆的這個特***,常用來實作優先隊列,堆的存取是随意,這就如同我們在圖書館的書架上取書,雖然書的擺放是有順序的,但是我們想取任意一本時不必像棧一樣,先取出前面所有的書,書架這種機制不同于箱子,我們可以直接取出我們想要的書。

記憶體配置設定中的棧和堆

    然而我要說的重點并不在這,我要說的堆和棧并不是資料結構的堆和棧,之是以要說資料結構的堆和棧是為了和後面我要說的堆區和棧區差別開來,請大家一定要注意。

    下面就說說C語言程式記憶體配置設定中的堆和棧,這裡有必要把記憶體配置設定也提一下,大家不要嫌我啰嗦,一般情況下程式存放在Rom或Flash中,運作時需要拷到記憶體中執行,記憶體會分别存儲不同的資訊,如下圖所示:

    記憶體中的棧區處于相對較高的位址以位址的增長方向為上的話,棧位址是向下增長的。

棧中配置設定局部變量空間,堆區是向上增長的用于配置設定程式員申請的記憶體空間。另外還有靜态區是配置設定靜态變量,全局變量空間的;隻讀區是配置設定常量和程式代碼空間的;以及其他一些分區。

來看一個網上很流行的經典例子:

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);  堆    

}

0.申請方式和回收方式不同

    不知道你是否有點明白了,堆和棧的第一個差別就是申請方式不同:棧(英文名稱是stack)是系統自動配置設定空間的,例如我們定義一個 char a;系統會自動在棧上為其開辟空間。而堆(英文名稱是heap)則是程式員根據需要自己申請的空間,例如malloc(10);開辟十個位元組的空間。由于棧上的空間是自動配置設定自動回收的,是以棧上的資料的生存周期隻是在函數的運作過程中,運作後就釋放掉,不可以再通路。而堆上的資料隻要程式員不釋放空間,就一直可以通路到,不過缺點是一旦忘記釋放會造成記憶體洩露。還有其他的一些差別我認為網上的朋友總結的不錯這裡轉述一下:

1.申請後系統的響應

棧:隻要棧的剩餘空間大于所申請空間,系統将為程式提供記憶體,否則将報異常提示棧溢出。

堆:首先應該知道作業系統有一個記錄空閑記憶體位址的連結清單,當系統收到程式的申請時,會周遊該連結清單,尋找第一個空間大于所申請空間的堆。

    結點,然後将該結點從空閑結點連結清單中删除,并将該結點的空間配置設定給程式,另外,對于大多數系統,會在這塊記憶體空間中的首位址處記錄本次配置設定的大小,這樣,代碼中的 delete語句才能正确的釋放本記憶體空間。另外,由于找到的堆結點的大小不一定正好等于申請的大小,系統會自動的将多餘的那部分重新放入空閑連結清單中。    

    也就是說堆會在申請後還要做一些後續的工作這就會引出申請效率的問題。

2.申請效率的比較

根據第0點和第1點可知。

棧:由系統自動配置設定,速度較快。但程式員是無法控制的。

堆:是由new配置設定的記憶體,一般速度比較慢,而且容易産生記憶體碎片,不過用起來最友善。

3.申請大小的限制

棧:在Windows下,棧是向低位址擴充的資料結構,是一塊連續的記憶體的區域。這句話的意思是棧頂的位址和棧的最大容量是系統預先規定好的,在 WINDOWS下,棧的大小是2M(也有的說是1M,總之是一個編譯時就确定的常數),如果申請的空間超過棧的剩餘空間時,将提示overflow。是以,能從棧獲得的空間較小。    

堆:堆是向高位址擴充的資料結構,是不連續的記憶體區域。這是由于系統是用連結清單來存儲的空閑記憶體位址的,自然是不連續的,而連結清單的周遊方向是由低位址向高位址。堆的大小受限于計算機系統中有效的虛拟記憶體。由此可見,堆獲得的空間比較靈活,也比較大。

4.堆和棧中的存儲内容

由于棧的大小有限,是以用子函數還是有實體意義的,而不僅僅是邏輯意義。

棧: 在函數調用時,第一個進棧的是主函數中函數調用後的下一條指令(函數調用語句的下一條可執行語句)的位址,然後是函數的各個參數,在大多數的C編譯器中,參數是由右往左入棧的,然後是函數中的局部變量。注意靜态變量是不入棧的。    

    當本次函數調用結束後,局部變量先出棧,然後是參數,最後棧頂指針指向最開始存的位址,也就是主函數中的下一條指令,程式由該點繼續運作。      

堆:一般是在堆的頭部用一個位元組存放堆的大小。堆中的具體内容有程式員安排。

關于存儲内容還可以參考這道題。這道題還涉及到局部變量的存活期。

5.存取效率的比較

關于堆和棧差別的比喻