堆棧瞭解

【FROM:百科&&網絡上尋找的貼子加以整理,加深瞭解】

heap && stack

我們經常會說堆棧怎麼樣怎麼樣,其實堆和棧是兩種不同的數據結構,它們之間還是有很大的區別的,連着叫只是由於歷史原因。

首先先了解下棧(stack)。

In a LIFO data structure, the last element added to the structure must be the first one to be removed. 

堆和棧都是把數據按序排列的數據結構。

在棧中進行數據的存放,就像把數據放入箱子或桶中一樣,是一種“後進先出”的數據結構,也就是說先存放的後取,後存放的先取。這就如同我們要取出放在箱子裏面底下的東西(放入的比較早的物體當然就壓在底下了,),我們首先要移開壓在它上面的物體(放入的比較晚的物體)。

【FROM :百科

棧作爲一種數據結構,是一種只能在一端進行插入和刪除操作的特殊的線性表。它按照後進先出的原則存儲數據,先進入的數據被壓入棧底,最後的數據在棧頂,需要讀數據的時候從棧頂開始彈出數據(最後一個數據被第一個讀出來)。棧具有記憶作用,對棧的插入與刪除操作中,不需要改變棧底指針

允許進行插入和刪除操作的一端稱爲棧頂(top),另一端爲棧底(bottom);棧底固定,而棧頂浮動;棧中元素個數爲零時稱爲空棧。插入一般稱爲進棧(PUSH),刪除則稱爲退棧(POP)。棧也稱爲後進先出表。程序可以將數據壓入棧中,也可以將數據從棧頂彈出。在i386機器中,棧頂由稱爲esp的寄存器進行定位棧在程序的運行中有着舉足輕重的作用。最重要的是棧保存了一個函數調用時所需要的維護信息,這常常稱之爲堆棧幀或者活動記錄

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

------------------------------------------------------

C語言中的棧是

stack:是編譯器自動分配給變量,以及函數調用的時候所使用的一些空間。在WINDOWS下分配地址是由高地址向低地址減少的,是一塊連續的內存區域(VS調試的時候查看變量內存的地址即可發現),在WINDOWS下棧的空間是1M(也有說2M的)。如果申請的空間超過棧剩餘的空間,則會棧溢出overflow,因此從棧上獲得的空間較小。

heap:是由malloc/new之類函數分配的空間所在地。地址是由低地址向高地址增長的,地址塊是分散的。

-----------------------------------------------------------

一、一個由C/C++編譯的程序佔用的內存分爲以下幾個部分:

1、棧區(stack)- 由編譯器自動分配釋放 ,存放函數的參數值,局部變量的值等。其操作方式類似於數據結構中的棧。

2、堆區(heap) - 一般由程序員分配釋放, 若程序員不釋放,程序結束時可能由OS回收 。注意它與數據結構中的堆是兩回事,分配方式倒是類似於鏈表

3、全局區(靜態區)(static)-,全局變量和靜態變量的存儲是放在一塊的,初始化的全局變量和靜態變量在一塊區域, 未初始化的全局變量和未初始化的靜態變量在相鄰的另一塊區域。 - 程序結束後有系統釋放

4、文字常量區 -常量字符串就是放在這裏的。 程序結束後由系統釋放

5、程序代碼區 -存放函數體的二進制代碼。

下面是網上一個一個前輩寫的例子

#include <stdio.h>
#include <conio.h>
#include <malloc.h>
int a=0;  //全局初始化區
char *p1; //全局未初始化區
int main(void)
{
	int b;              //棧
	char s[]="abc";     //棧
	char *p2;           //棧
	char *p3="123456";  //123456在常量區,p3在棧上
	static int c=0;     //全局(靜態)初始化區
	p1=(char *)malloc(10);//堆
	p2=(char *)malloc(20);//堆
	getch();
	return 0;

}


二、堆和棧的理論知識

2.1 申請方式

stack:由系統自行分配,速度較快,但是程序員無法控制。棧上的數據的生存週期只是在函數的運行過程中,運行後就釋放掉,不可以再訪問。

heap:需要程序員申請並指明大小,在c中用malloc,在c++中用new申請。一般速度比較慢,而且容易產生內存碎片,不過用起來最方便。而堆上的數據在程序員來釋放之前一直可以訪問,但是如果忘記釋放,則會造成內存泄露。
2.2 申請後系統的響應

stack:只要棧的剩餘空間大於所申請空間,系統將爲程序提供內存,否則將報異常提示棧溢出。

heap :首先應該知道操作系統有一個記錄空閒內存地址的鏈表,當系統收到程序的申請時,會遍歷該鏈表,尋找第一個空間大於所申請空間的堆結點,然後將該結點從空閒結點鏈表中刪除,並將該結點的空間分配給程序,另外,對於大多數系統,會在這塊內存空間中的首地址處記錄本次分配的大小,這樣,代碼中的delete/free語句才能正確的釋放本內存空間。另外,由於找到的堆結點的大小不一定正好等於申請的大小,系統會自動的將多餘的那部分重新放入空閒鏈表中。這也就是爲什麼動態申請內存容易造成內存碎片的產生原因。所以分配內存時你會發現他們的地址不連續。

2.3 申請大小的限制

stack:棧頂的地址和棧的最大容量是系統預先規定好的,在 WINDOWS下,棧的大小是2M(也有的說是1M,總之是一個編譯時就確定的常數),如果申請的空間超過棧的剩餘空間時,將提示overflow。因此,能從棧獲得的空間較小。

heap :堆是向高地址擴展的數據結構,是不連續的內存區域。這是由於系統是用鏈表來存儲的空閒內存地址的,自然是不連續的,而鏈表的遍歷方向是由低地址向高地址。堆的大小受限於計算機系統中有效的虛擬內存。由此可見,堆獲得的空間比較靈活,也比較大。

2.4 棧和堆中的內容

stack:在函數調用時,第一個進棧的是主函數中後的下一條指令(函數調用語句的下一條可執行語句)的地址,然後是函數的各個參數,在大多數的C編譯器中,參數是由右往左入棧的,然後是函數中的局部變量。注意靜態變量是不入棧的。當本次函數調用結束後,局部變量先出棧,然後是參數,最後棧頂指針指向最開始存的地址,也就是主函數中的下一條指令,程序由該點繼續運行。

heap :一般是在堆的頭部用一個字節存放堆的大小。堆中的具體內容有程序員安排。

2.5 存取效率比較

char s1[] = "aaaaaaaaaaaaaaa";

char *s2 = "bbbbbbbbbbbbbbbbb";

aaaaaaaaaaa是在運行時刻賦值的;

而bbbbbbbbbbb是在編譯時就確定的;

但是,在以後的存取中,在棧上的數組比指針所指向的字符串(例如堆)快。反彙編查到具體操作。

2.6小結

堆和棧的區別可以用如下的比喻來看出

使用棧就象我們去飯館裏吃飯,只管點菜(發出申請)、付錢、和吃(使用),吃飽了就走,不必理會切菜、洗菜等準備工作和洗碗、刷鍋等掃尾工作,他的好處是快捷,但是自由度小。

使用堆就象是自己動手做喜歡吃的菜餚,比較麻煩,但是比較符合自己的口味,而且自由度大。

堆和棧的區別主要分:

操作系統方面的堆和棧,如上面說的那些,不多說了。

還有就是數據結構方面的堆和棧,這些都是不同的概念。這裏的堆實際上指的就是(滿足堆性質的)優先隊列的一種數據結構,第1個元素有最高的優先權;棧實際上就是滿足先進後出的性質的數學或數據結構。


三、內存在靜態區創建

如全局變量、static變量

這塊內存也是連續的,也像一個數組,但它跟棧上創建內存唯一的區別是,內存作用時間不一樣,靜態區內存作用時間是整個“程序”運行時間,棧上內存作用時間是整個“函數”的運行時間,注意“程序”和“函數”的區別
而堆內存作用時間範圍是0到整個“程序”運行時間,如果你要在小於整個“程序”運行時間內釋放這塊內存的話,就要使用free,所以是手動申請手動釋放,你自己可以控制,但是寫代碼的好習慣習慣是程序中有幾個malloc就有幾個free,這樣可以防止內存泄露







發佈了90 篇原創文章 · 獲贊 68 · 訪問量 66萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章