一、c語言五大內存分區
-
棧區(stack):存放函數形參和局部變量(auto類型),由編譯器自動分配和釋放
-
堆區(heap):該區由程序員申請后使用,需要手動釋放否則會造成內存泄漏。如果程序員沒有手動釋放,那么程序結束時可能由OS回收。
-
全局/靜態存儲區:存放全局變量和靜態變量(包括靜態全局變量與靜態局部變量),初始化的全局變量和靜態局部變量放在一塊,未初始化的放在另一塊
-
文字常量區:常量在統一運行被創建,常量區的內存是只讀的,程序結束后由系統釋放。
-
程序代碼區:存放程序的二進制代碼,內存由系統管理
二、可執行程序程序三段-Text段,Date段,Bss段
1.一個程序的3個基本段:text段,dtae段,bss段
- text段在內存中被映射為只讀,但date段與bss段是可寫的
- text段:代碼段,就是放程序代碼的,編譯時確定,只讀
- date段:存放在編譯階段(而非運行時)就能確定的數據,可讀可寫。也就是通常所說的靜態存儲區,賦了初值的全局變量和賦初值的靜態變量存放在這個區域,常量也存在這個區域
- bss段:已經定義但沒賦初值的全局變量和靜態變量存放在這個區域。
兩者之間區別是:代碼段,數據段,堆棧段是cpu級別的概念,五大分區屬於語言級別的概念,兩者是不同的概念。
三、可執行程序內存空間與邏輯地址空間的映射與划分
- 左邊是UNIX系統的執行文件,右邊是進程對應的邏輯地址空間的划分情況
- 首先是棧區(堆棧區stack),堆棧是由編譯器自動分配釋放,存放函數的參數和局部變量的值(auto類型),操作方式類似於數據結構中的棧。棧的申請是由系統自動分配,如在函數內部申請一個局部變量int h,同時判斷所申請空間是否小於棧的剩余空間,如果小於則為其開辟空間,為程序提供內存,否則將報異常提示棧溢出。
- 堆(heap),堆一般由程序員分配釋放,若程序員不釋放,程序結束可能由OS回收。它與數據結構中的堆是兩回事,分配方式類似於鏈表,申請則是程序員自己操作使用malloc或new。申請過程比較復雜,當系統收到程序的申請時,會遍歷記錄空閑內存地址的鏈表,以求尋找第一個空間大於所申請空間的堆節點,然后將該節點從空閑節點鏈表中刪除,並將該節點的空間分配給程序,有些情況下,新申請的內存塊的首地址記錄本次分配的內存塊的大小,這樣在delete尤其是delete[]時能正確的釋放內存空間。
- 下邊是全局靜態存儲區,全局變量與靜態變量的存儲是放在一塊的,初始化的全局變量與靜態變量存放在一塊區域,未初始化的全局變量與未初始化的靜態變量存放在相鄰的另一塊區域。
- 文字常量區,常量字符串就是放在該部分,只讀存儲區,程序結束后由系統釋放
- 程序代碼區,存放程序的二進制代碼區。
四、存儲類型關鍵字定義變量與函數作用域與生命周期
- auto變量:函數的局部變量,如果沒有聲明為static,函數中定義的局部變量全部為auto類型,auto變量包括未加static聲明的局部變量和函數的形參。在函數調用時系統會給他們分配存儲空間,在函數調用結束后會自動釋放這些空間。屬於動態存儲方式。
- static變量:用static聲明的局部變量在調用結束后不會消失而保存原來的值。static局部變量定義使用后值會存儲下來。所以使用static局部變量定義只需要一次賦值。靜態局部變量的作用域僅限於所定義的函數。但函數結束后變量的值會保留。直到整個程序運行結束。全局變量從定義開始作用於整個文件直至程序運行結束。
- register寄存器變量:寄存器變量可以提高c語言的執行效率,即將局部變量的值存入CPU的寄存器中。需要注意的是!!!:1.只有動態存儲的變量(自動局部變量和形參)才可以作為寄存器變量來存儲,局部靜態變量不可以定義為寄存器變量。2.計算機的寄存器數目是有限的,所以不能定義任意多個寄存器變量。
- extern外部變量:即全局變量的外部表現形式,是在函數外部定義的變量。全局變量的作用域為從定義開始到源文件結束。exten對該變量作外部變量聲明,擴展變量作用域。
五、堆與棧的區別
1.申請方式
- stack:棧;由系統自動分配,自動開辟空間
- heap:由程序員自己申請並指明大小,c中malloc,c++中new。如p1=(char*)malloc(10);p2=(char*)new(10);但需要注意的是p1,p2本事是在棧中的
2.申請后系統的響應
- 棧:只要棧的剩余空間大於所申請空間,系統將為程序提供內存,否則將報異常提示棧溢出
- 堆:首先操作系統有一個記錄空閑內存地址的鏈表,當系統收到程序的申請時,會遍歷該鏈表,尋找第一個大於所申請空間的堆節點,然后將該節點從空閑節點鏈表中刪除,並將該節點的空間分配給程序。另外對於大部分系統,會在這塊內存空間中的首地址處記錄本次分配的大小,這樣代碼中的delete語句才能正確的釋放本內存空間。另外由於找到的堆節點大小不一定正好等於申請的大小,系統會自動的將多余的那部分重新放入空閑鏈表中。
3.申請大小的限制
- 棧:在windows下棧是向低地址擴展的數據結構,是一塊連續的內存區域。所以棧的棧頂地址和最大容量是系統預先設定好的。在windows下棧的大小是2M.因此能從棧獲得的空間比較小。
- 堆:堆是向高地址擴展的數據結構,是不連續的內存區域。這是是由於系統用鏈表來存儲空閑內存地址的,所以是不連續的。而鏈表的遍歷方向是由低地址到高地址。堆得大小受限於計算機系統中有效的虛擬內存大小。相比較而言堆獲得的空間比較靈活,也比較大。
4.申請效率的比較
- 棧:由系統自動分配,速度較快,但程序員是無法控制的。
- 堆:由new分配的內存,一般速度比較慢,而且比較容易產生內存碎片,不過用起來最方便。
5.堆和棧中的存儲內容
- 棧:在函數調用時,第一個進棧的是主函數中的下一條指令(函數調用語句的下一條可執行語句)的地址,然后是函數的各個參數。在大多數c編譯器中,參數是由右往左壓棧的,然后是函數中的局部變量。靜態變量是不入棧的。當函數調用結束后,局部變量先出棧,然后是參數,最后棧頂指針指向最開始存的地址,,也就是主函數的下一條指令,程序由該點繼續執行。
- 堆:一般是在堆的頭部用一個字節存放堆得大小,其他內容自己安排。
6.存取效率的比較
-
1 char str1[]="aaaaaa"; 2 char *str2="cccccc";
- 第一行是在運行時刻賦值的,第二行是在編譯時就已經確定的,但在以后的存取過程中,在棧上的數組比指針指向的字符串快。