***************************************************
更多精彩,歡迎進入:http://shop115376623.taobao.com
***************************************************
以下詳解來自:
http://blog.163.com/zb_075/blog/static/37340328201151102756835/
http://blog.csdn.net/yitian20000/article/details/6358837
雖然這些博客寫的東西都是天下一把抄的,但是對于我們來說,只是來潛心學習就好。
一、預備知識—程序的內存分配??
??一個由C/C++編譯的程序占用的內存分為以下幾個部分??
??1、棧區(stack)—???由編譯器自動分配釋放???,存放函數的參數值,局部變量的值等。其??
??操作方式類似于數據結構中的棧。??
??2、堆區(heap)???—???一般由程序員分配釋放,???若程序員不釋放,程序結束時可能由OS回??
??收???。注意它與數據結構中的堆是兩回事,分配方式倒是類似于鏈表,呵呵。??
??3、全局區(靜態區)(static)—,全局變量和靜態變量的存儲是放在一塊的,初始化的??
??全局變量和靜態變量在一塊區域,???未初始化的全局變量和未初始化的靜態變量在相鄰的另??
??一塊區域。???-???程序結束后由系統釋放。??
??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"優化成一個地方。}
在函數體中定義的變量通常是在棧上,
用malloc, calloc, realloc等分配內存的函數分配得到的就是在堆上。
在所有函數體外定義的是全局量,加了static修飾符后不管在哪里都存放在全局區(靜態區),
在所有函數體外定義的static變量表示在該文件中有效,不能extern到別的文件用,
在函數體內定義的static表示只在該函數體內有效。另外,函 數中的 "adgfdf "這樣的字符串存放在常量區。?
還有就是函數調用時會 在棧上有一系列的保留現場及傳遞參數的操作。棧的空間大小有限定,vc的缺省是2M。棧不夠用的情況一般是程序中分配了大量數組和遞歸函數層次太深。有一 點必須知道,當一個函數調用完返回后它會釋放該函數中所有的棧空間。棧是由編譯器自動管理的,不用你操心。堆是動態分配內存的,并且你可以分配使用很大的 內存。但是用不好會產生內存泄漏。并且頻繁地malloc和free會產生內存碎片(有點類似磁盤碎片),因為c分配動態內存時是尋找匹配的內存的。而用 棧則不會產生碎片。在棧上存取數據比通過指針在堆上存取數據快些
堆(heap) 和棧(stack)是C/C++編程不可避免會碰到的兩個基本概念。首先,這兩個概念都可以在講數據結構的書中找到,他們都是基本的數據結構,雖然棧更為 簡單一些。在具體的C/C++編程框架中,這兩個概念并不是并行的。對底層機器代碼的研究可以揭示,棧是機器系統提供的數據結構,而堆則是C/C++函數 庫提供的。具體地說,現代計算機(串行執行機制),都直接在代碼底層支持棧的數據結構。這體現在,有專門的寄存器指向棧所在的地址,有專門的機器指令完成 數據入棧出棧的操作。這種機制的特點是效率高,支持的數據有限,一般是整數,指針,浮點數等系統直接支持的數據類型,并不直接支持其他的數據結構。
因為棧 的這種特點,對棧的使用在程序中是非常頻繁的。對子程序的調用就是直接利用棧完成的。機器的call指令里隱含了把返回地址推入棧,然后跳轉至子程序地址 的操作,而子程序中的ret指令則隱含從堆棧中彈出返回地址并跳轉之的操作。C/C++中的自動變量是直接利用棧的例子,這也就是為什么當函數返回時,該 函數的自動變量自動失效的原因。?
? ? 和棧不同,堆的數據結構并不是由系統(無論是機器系統還是操作系統)支持的,而是由函數庫提供的。基本的 malloc/realloc/free函數維護了一套內部的堆數據結構。當程序使用這些函數去獲得新的內存空間時,這套函數首先試圖從內部堆中尋找可用 的內存空間,如果沒有可以使用的內存空間,則試圖利用系統調用來動態增加程序數據段的內存大小,新分配得到的空間首先被組織進內部堆中去,然后再以適當的 形式返回給調用者。當程序釋放分配的內存空間時,這片內存空間被返回內部堆結構中,可能會被適當的處理(比如和其他空閑空間合并成更大的空閑空間),以更 適合下一次內存分配申請。這套復雜的分配機制實際上相當于一個內存分配的緩沖池(Cache),使用這套機制有如下若干原因:?
1. 系統調用可能不支持任意大小的內存分配。有些系統的系統調用只支持固定大小及其倍數的內存請求(按頁分配);這樣的話對于大量的小內存分類來說會造成浪費。?
2. 系統調用申請內存可能是代價昂貴的。系統調用可能涉及用戶態和核心態的轉換。?
3. 沒有管理的內存分配在大量復雜內存的分配釋放操作下很容易造成內存碎片。
??三、堆和棧的理論知識????
??2.1申請方式????
??stack:????
??由系統自動分配。???例如,聲明在函數中一個局部變量???int???b;???系統自動在棧中為b開辟空??
??間????
??heap:????
??需要程序員自己申請,并指明大小,在c中malloc函數????
??如p1???=???(char???*)malloc(10);????
??在C++中用new運算符????
??如p2???=???new???char[10];????
??但是注意p1、p2本身是在棧中的。
??2.2?申請后系統的響應????
??棧:只要棧的剩余空間大于所申請空間,系統將為程序提供內存,否則將報異常提示棧溢??
??出。????
??堆:首先應該知道操作系統有一個記錄空閑內存地址的鏈表,當系統收到程序的申請時,??
??會遍歷該鏈表,尋找第一個空間大于所申請空間的堆結點,然后將該結點從空閑結點鏈表??
??中刪除,并將該結點的空間分配給程序,另外,對于大多數系統,會在這塊內存空間中的??
??首地址處記錄本次分配的大小,這樣,代碼中的delete語句才能正確的釋放本內存空間。??
??另外,由于找到的堆結點的大小不一定正好等于申請的大小,系統會自動的將多余的那部??
??分重新放入空閑鏈表中。
??2.3申請大小的限制????
??棧:在Windows下,棧是向低地址擴展的數據結構,是一塊連續的內存的區域。這句話的意??
??思是棧頂的地址和棧的最大容量是系統預先規定好的,在WINDOWS下,棧的大小是2M(也有??
??的說是1M,總之是一個編譯時就確定的常數),如果申請的空間超過棧的剩余空間時,將??
??提示overflow。因此,能從棧獲得的空間較小。????
??堆:堆是向高地址擴展的數據結構,是不連續的內存區域。這是由于系統是用鏈表來存儲??
??的空閑內存地址的,自然是不連續的,而鏈表的遍歷方向是由低地址向高地址。堆的大小??
??受限于計算機系統中有效的虛擬內存。由此可見,堆獲得的空間比較靈活,也比較大。
??2.4申請效率的比較:????
??棧由系統自動分配,速度較快。但程序員是無法控制的。????
??堆是由new分配的內存,一般速度比較慢,而且容易產生內存碎片,不過用起來最方便.????
??另外,在WINDOWS下,最好的方式是用VirtualAlloc分配內存,他不是在堆,也不是在棧是??
??直接在進程的地址空間中保留一塊內存,雖然用起來最不方便。但是速度快,也最靈活。
??2.5堆和棧中的存儲內容????
??棧:???在函數調用時,第一個進棧的是主函數中后的下一條指令(函數調用語句的下一條可??
??執行語句)的地址,然后是函數的各個參數,在大多數的C編譯器中,參數是由右往左入棧??
??的,然后是函數中的局部變量。注意靜態變量是不入棧的。????
??當本次函數調用結束后,局部變量先出棧,然后是參數,最后棧頂指針指向最開始存的地??
??址,也就是主函數中的下一條指令,程序由該點繼續運行。????
??堆:一般是在堆的頭部用一個字節存放堆的大小。堆中的具體內容由程序員安排。
??2.6存取效率的比較????
??char???s1[]???=???"aaaaaaaaaaaaaaa";????
??char???*s2???=???"bbbbbbbbbbbbbbbbb";????
??aaaaaaaaaaa是在運行時刻賦值的;????
??而bbbbbbbbbbb是在編譯時就確定的;????
??但是,在以后的存取中,在棧上的數組比指針所指向的字符串(例如堆)快。????
??比如:????
??#include????
??void???main()????
??{????
??char???a???=???1;????
??char???c[]???=???"1234567890";????
??char???*p???="1234567890";????
??a???=???c[1];????
??a???=???p[1];????
??return;????
??}????
??對應的匯編代碼????
??10:???a???=???c[1];????
??00401067???8A???4D???F1???mov???cl,byte???ptr???[ebp-0Fh]????
??0040106A???88???4D???FC???mov???byte???ptr???[ebp-4],cl????
??11:???a???=???p[1];????
??0040106D???8B???55???EC???mov???edx,dword???ptr???[ebp-14h]????
??00401070???8A???42???01???mov???al,byte???ptr???[edx+1]????
??00401073???88???45???FC???mov???byte???ptr???[ebp-4],al????
??第一種在讀取時直接就把字符串中的元素讀到寄存器cl中,而第二種則要先把指針值讀到??
??edx中,再根據edx讀取字符,顯然慢了。
??2.7小結:????
??堆和棧的區別可以用如下的比喻來看出:????
??使用棧就象我們去飯館里吃飯,只管點菜(發出申請)、付錢、和吃(使用),吃飽了就??
??走,不必理會切菜、洗菜等準備工作和洗碗、刷鍋等掃尾工作,他的好處是快捷,但是自??
??由度小。????
??使用堆就象是自己動手做喜歡吃的菜肴,比較麻煩,但是比較符合自己的口味,而且自由??
??度大。???(經典!)
??2.8 對比 從以 上知識可知,棧是系統提供的功能,特點是快速高效,缺點是有限制,數據不靈活;而棧是函數庫提供的功能,特點是靈活方便,數據適應面廣泛,但是效率有一定 降低。棧是系統數據結構,對于進程/線程是唯一的;堆是函數庫內部數據結構,不一定唯一。不同堆分的內存無法互相操作。棧空間分靜態分配和動態分配兩種。 靜態分配是編譯器完成的,比如自動變量(auto)的分配。動態分配由alloca函數完成。棧的動態分配無需釋放(是自動),也就沒有釋放函數。為可移 植的程序起見,棧的動態分配操作是不被鼓勵的!堆空間的分配總是動態的,雖然程序結束時所有的數據空間都會被釋放回系統,但是精確的申請內存/釋放內存匹 配是良好程序的基本要素。?
?操作系統方面的堆和棧,如上面說的那些,不多說 了。還有就是數據結構方面的堆和棧,這些都是不同的概念。這里的堆實際上指的就是(滿足堆性質的)優先隊列的一種數據結構,第1個元素有最高的優先權;棧 實際上就是滿足先進后出的性質的數學或數據結構。雖然堆棧,堆棧的說法是連起來叫,但是他們還是有很大區別的,連著叫只是由于歷史的原因。
堆和棧的生長方向恰好相反,?
|--------------| 低地址?
| 堆 |?
|--------------|?
| | |?
| I |?
| |?
| ^ |?
| 棧 | 高地址?
-----------------?
所以計算機中的堆和棧經常時放一塊講的?
一般不是必要就不要動態創建,最討厭把new出來的東西當局部變量用,用萬了馬上delete 的做法.?
理由?
1.棧分配比堆快,只需要一條指令就呢給配所有的局部變量?
2.棧不會出現內存碎片?
3.棧對象好管理?
當然,某些情況下也要那么寫,比如?
1.對象很大?
2.對象需要在某個特定的時刻構造或析夠?
3.類只允許對象動態創建,比如VCL的大多數類?
當然,必須用堆對象時也不能躲避
堆內存和棧內存各有什么作用?堆:順序隨意 ??棧:先進后出
----------------------------------------------------------------------------------------
為什么說在堆上分配?內存?比在棧上分配?內存?慢?堆空間的開辟需要用系統函數,棧上直接修改指針?
堆空間的管理?需 要系統記帳,棧上的空間可以由編譯器管理或是保存在某個處理器寄存器中。?
堆空間的釋放需要系統管理,棧上的釋放可以直接丟棄。堆空間需要通過棧上的指針?間接引用,所以訪問會慢?
記 得在apue2上面看到關于線程中有這樣一段話,大致意思是,一個 線程有自己的堆棧,可以在堆棧上分配?內存?,比如說一個結構體,如果這個線程調用了pthread_exit()返回這個結構體指針?的時候之后要特別 的小心,因為很有可能這個結構體里面的成員值發生改變,這個可以理解,因為同一個進程所 有線程的資源是共享的,當這個線程退出之后那部分以前用過的堆棧很可能被其它線程占用,但同時又說如果malloc就不會出現這樣的問題,?
比如,在棧上分一個int,只要esp-4就可以了,?
在堆上系統要記錄被分配?內存?的信息,以便釋放?
----------------------------------?
內存?分配?方式有三種: ???
?????
???1.從靜態存儲區域分配?。內存?在程序編譯的時候就已經分配?好,這塊內存?在程序的整個運行期間都存在。例如全局變 量,static變量。 ???
?????
???2.在棧上創建。在執行函數時,函數內局部變量的存儲單元?都可以在棧上創建,函數執行結束時這些存儲單元?自動被 釋放。棧內存?分配?運算內置于處理器的指令集中,效率很 高,但是分配?的內存?容量有限。 ???
?????
???3.從堆上分配?,亦稱動態內存?分配?。程序在運行的時候用malloc或new申請任意多少的內存?,程 序員自己負責在何時用free或delete釋放內存?。動態內存?的生存期由我們決定,使用非常靈活,但問題也最多。?
----------------------------------------?
一般所說的堆棧(stack)往往是指棧,先進后出,?它是一塊內存?區。用以存放程序的局部變量,臨時變量,函數的參數,返回地址等。在這塊區域中的變量的分配?和釋放由系統自動進行。不需要用戶的參與。 ???
???而在堆(heap,先進先出)?上的空間則是由用戶進行分配?,并由用 戶負責釋放。?
=========================================================?
以下為自己再此測驗過程:
liuguanwen@liuguanwen-OptiPlex-990:~/lgw$vi hellopioneer.c
#include
int def;
char ch0;
static double efg;
float flo;
char ch1;
char ch2;
double bac = 0;
double gfe = 0;
static int hoo;
char ch3;
char ch4='0';
int ghk;
int main (int argc, char *argv[])
{
????????int abc = 0;
????printf("abc:%pn",&abc);
????printf("argc:%pn",&argc);
????printf("argv:%pnnn",&argv);
????printf("def:%pn",&def);
????printf("ch0:%pn",&ch0);
????printf("efg:%pn",&efg);
????printf("flo:%pnn",&flo);
????printf("ch1:%pn",&ch1);
????printf("ch2:%pnn",&ch2);
????printf("bac:%pn",&bac);
????printf("gfe:%pn",&gfe);
????printf("hoo:%pnn",&hoo);
????printf("ch3:%pn",&ch3);
????printf("ch4:%pn",&ch4);
????printf("ghk:%pnn",&ghk);
????char * str = "hello world!n";
????printf("st0:%pn",&str);
????printf("st1:%pn",str);
????printf("st2:%pn","hello world!n");
????printf("st3:%pn","hello world!n");
????static int hello;
????printf("hello:%pn",&hello);
????const int tni = 5;
????printf("tni:%pn",&tni);
????????return 0;
}
:q
liuguanwen@liuguanwen-OptiPlex-990:~/lgw$ gcc hellopioneer.c
liuguanwen@liuguanwen-OptiPlex-990:~/lgw$ l
a.out*??changeip???hellopioneer.c??TableEnv/???????資料/
Book/???changeip0??ShangHai????????Work/???????????座位表.xls
CCALIB??embedded/??Study/??????????熊朝川簡歷.doc
liuguanwen@liuguanwen-OptiPlex-990:~/lgw$ ./a.out
abc:0xbfe47a9c
argc:0xbfe47ab0
argv:0xbfe47ab4
def:0x804a04c
ch0:0x804a049
efg:0x804a030
flo:0x804a044
ch1:0x804a048
ch2:0x804a03c
bac:0x804a020
gfe:0x804a028
hoo:0x804a038
ch3:0x804a04a
ch4:0x804a014
ghk:0x804a040
st0:0xbfe47a98
st1:0x80486d0
st2:0x80486d0
st3:0x80486d0
hello:0x804a03c
tni:0xbfe47a94
liuguanwen@liuguanwen-OptiPlex-990:~/lgw$
以上是以gcc來編譯運行后的結果,從中也可以推斷出些結果。當然一開始是用Qt來研究(Qt編譯實質也就是gcc),之后gcc只是拿來驗證const類型的變量是放在哪個內存區。Qt中代碼如下:
#include
#include
int def;
char ch0;
static double efg;
float flo;
char ch1;
char ch2;
double bac = 0;
double gfe = 0;
static int hoo;
char ch3;
char ch4='0';
int ghk;
int main(int argc, char *argv[])
{
????QCoreApplication a(argc, argv);
????int abc = 0;
????printf("abc:%pn",&abc);
????printf("argc:%pn",&argc);
????printf("argv:%pnnn",&argv);
????printf("def:%pn",&def);
????printf("ch0:%pn",&ch0);
????printf("efg:%pn",&efg);
????printf("flo:%pnn",&flo);
????printf("ch1:%pn",&ch1);
????printf("ch2:%pnn",&ch2);
????printf("bac:%pn",&bac);
????printf("gfe:%pn",&gfe);
????printf("hoo:%pnn",&hoo);
????printf("ch3:%pn",&ch3);
????printf("ch4:%pn",&ch4);
????printf("ghk:%pnn",&ghk);
????char * str = "hello world!n";
????printf("st0:%pn",&str);
????printf("st1:%pn",str);
????printf("st2:%pn","hello world!n");
????printf("st3:%pn","hello world!n");
????static int hello;
????printf("hello:%pn",&hello);
????const int tni = 5;
????printf("tni:%pn",&tni);
????return a.exec();
}
輸出結果:
abc:0xbfe0e89c
argc:0xbfe0e8c0
argv:0xbfe0e8c4
def:0x804a038
ch0:0x804a03c
efg:0x804a060
flo:0x804a040
ch1:0x804a044
ch2:0x804a045
bac:0x804a048
gfe:0x804a050
hoo:0x804a068
ch3:0x804a058
ch4:0x804a028
ghk:0x804a05c
st0:0xbfe0e898
st1:0x8048ae0
st2:0x8048ae0
st3:0x8048ae0
hello:0x804a06c
tni:0xbfe0e894
早先曉得C在變量內存分配中,分四片區(代碼區/常量區/棧區/堆區)Code/Const/Stack/Heap
代碼區從名字中,就可已曉得,是存放代碼的;
常量區也可以從文字中看出,存的是常量,哪些是屬于常量,那就得細分下了,比如說char * abc = "hello world!",這個常量串串"hello world!"是存在常量區的(不可改變),而指針變量abc是存在棧區的(當然這個不是malloc/new出來的一部分);
棧區,是大伙兒經常用到的,我int i;這個局部變量啊就是的;
堆區嘛,剛才也說咧,new/malloc出來的東西都存放在這兒,變量及對象都是;
全局區(靜態區)經過以上的驗證,全局變量及靜態變量分在內存的一個區間,只不過在分配時有先后順序而已,全局變量在先,靜態變量在后分配而已。
在這里需要注意一點:屬于類的變量(static的),及屬于對象的變量,那是存在不同的內存區的,這點C與C++在內存分配上可以說是一樣的。
“這點”指的是內存分配上(這點C與C++在內存分配上可以說是一樣的) 表達上有點混淆哈,木看懂的還望指出及還望指點指點。