關於C++內存分區的學習


在學習C++過程中,對其內存的分區和各種變量、常量的定義及存放,並考慮其生命週期,很是頭疼。今天根據自己的理解和查閱各個資料,整理記錄了下,但還是不夠詳細,請大家斧正!

直接繪製出一個分析圖:


代碼區存放程序體的二進制代碼。比如我們寫的函數,都是在代碼區的。

全局數據區:全局、靜態數據存放在一起的,初始化的全局變量和靜態變量是在一起的。未初始化的全局變量和靜態變量是在相鄰的空間中。

說明:全局變量和靜態全局變量的存儲方式是一致的,但是其區別在於,全局變量在整個源代碼中都可以使用,而靜態全局變量只能在當前文件中有效。比如我們的一個程序有5個文件,那麼某個文件中申請了靜態全局變量,這個靜態全局變量只能在當前文件中使用,其他四個文件均不可以使用。而某個文件中申請了全局變量,那麼其他四個文件中都可以使用該全局變量(只需要通過關鍵字extern申明一下就可以使用了)。事實上static改變了變量的作用範圍。

補充說明:對於全局變量,(1)在所有函數的外部(如在main()之前)定義;(2)在程序的每個函數中是可見的,這種可見的前提是定義了全局變量之後的函數;(3)存放在全局數據區內;(4)若不初始化,則爲0.

堆區: 一般由程序員分配釋放, 若程序員不釋放,程序結束時可能由OS回收 。注意它與數據結構中的堆是兩回事,分配方式倒是類似於鏈表 。在C中使用malloc,如p1   =   (char   *)malloc(10); 在C++中使用new(當然C++中也可以使用malloc)。如p2 = new  char[10];  但是注意p1、p2本身是在棧中的。 在申請堆時,首先應該知道操作系統有一個記錄空閒內存地址的鏈表,當系統收到程序的申請時, 會遍歷該鏈表,尋找第一個空間大於所申請空間的堆結點,然後將該結點從空閒結點鏈表中刪除,並將該結點的空間分配給程序,另外,對於大多數系統,會在這塊內存空間中的首地址處記錄本次分配的大小,這樣,代碼中的delete語句才能正確的釋放本內存空間。另外,由於找到的堆結點的大小不一定正好等於申請的大小,系統會自動的將多餘的那部分重新放入空閒鏈表中。堆是向高地址擴展的數據結構,是不連續的內存區域。這是由於系統是用鏈表來存儲空閒內存地址的,自然是不連續的,而鏈表的遍歷方向是由低地址向高地址。堆的大小受限於計算機系統中有效的虛擬內存。

棧區:由編譯器自動分配釋放 ,存放函數的參數值,局部變量的值等。其操作方式類似於數據結構中的棧。只要棧的剩餘空間大於所申請空間,系統將爲程序提供內存,否則將報異常提示棧溢出。    

說明:主要存放函數的參數以及局部變量。棧區由系統進行內存管理,在函數完成執行,系統自行釋放棧區內存,不需要用戶管理。整個程序的棧區的大小可以在編譯器中由用戶自行設定,默認的棧區大小爲2M,不同編譯器可能不同。在函數調用時,第一個進棧的是主函數中後的下一條指令(函數調用語句的下一條可執行語句)的地址,然後是函數的各個參數,在大多數的C編譯器中,參數是由右往左入棧的,然後是函數中的局部變量。注意靜態變量是不入棧的。當本次函數調用結束後,局部變量先出棧,然後是參數,最後棧頂指針指向最開始存的地址,也就是主函數中的下一條指令,程序由該點繼續運行。    

補充說明:棧區的數據若不初始化則爲任意值。

字符串常量區:存放字符串常量,程序結束後,由系統進行釋放。比如我們定義char * p = “Hello World”; 這裏的“Hello World”就是在字符串常量中,最終系統會自動釋放。


 結合一個例子:

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"優化成一塊。 

   總的來說,這次分析對內存分區的知識有所長進,但由於網上衆說紛紜,書上資料也是各有差異,具體細節還得在深入學習後進一步分析~

 


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章