以下內容,大部分整理自網絡
C分為四個區:堆,棧,靜態全局變量區,常量區
C++內存分為5個區域(堆棧全常代 ):
-
堆 heap :
由new分配的內存塊,其釋放編譯器不去管,由我們程序自己控制(一個new對應一個delete)。如果程序員沒有釋放掉,在程序結束時OS會自動回收。涉及的問題:“緩沖區溢出”、“內存泄露” -
棧 stack :
是那些編譯器在需要時分配,在不需要時自動清除的存儲區。存放局部變量、函數參數。
存放在棧中的數據只在當前函數及下一層函數中有效,一旦函數返回了,這些數據也就自動釋放了。 -
全局/靜態存儲區 (.bss段和.data段) :
全局和靜態變量被分配到同一塊內存中。在C語言中,未初始化的放在.bss段中,初始化的放在.data段中;在C++里則不區分了。 -
常量存儲區 (.rodata段) :
存放常量,不允許修改(通過非正當手段也可以修改) -
代碼區 (.text段) :
存放代碼(如函數),不允許修改(類似常量存儲區),但可以執行(不同於常量存儲區)
根據c/c++對象生命周期不同,c/c++的內存模型有三種不同的內存區域,即
- 自由存儲區,動態區、靜態區。
- 自由存儲區:局部非靜態變量的存儲區域,即平常所說的棧
- 動態區: 用operator new ,malloc分配的內存,即平常所說的堆
- 靜態區:全局變量 靜態變量 字符串常量存在位置
而代碼雖然占內存,但不屬於c/c++內存模型的一部分
在linux系統中,程序在內存中的分布如下所示:
低地址
.text---> .data --->.bss
--->heap(堆) --> unused <-- stack(棧)
-->env
高地址
其中 :
-
.text 部分是編譯后程序的主體,也就是程序的機器指令。
-
.data 和 .bss 保存了程序的全局變量,.data保存有初始化的全局變量,.bss保存只有聲明沒有初始化的全局變量。
-
heap(堆)中保存程序中動態分配的內存,比如C的malloc申請的內存,或者C++中new申請的內存。堆向高地址方向增長。
-
stack(棧)用來進行函數調用,保存函數參數,臨時變量,返回地址等。
BSS 是“Block Started by Symbol”的縮寫,意為“以符號開始的塊”。BSS是Unix鏈接器產生的未初始化數據段。其他的段分別是包含程序代碼的 “text”段和包含已初始化數據的“data”段。BSS段的變量只有名稱和大小卻沒有值。此名后來被許多文件格式使用,包括PE。“以符號開始的塊” 指的是編譯器處理未初始化數據的地方。BSS節不包含任何數據,只是簡單的維護開始和結束的地址,以便內存區能在運行時被有效地清零。BSS節在應用程序 的二進制映象文件中並不存在。
在采用段式內存管理的架構中(比如intel的80x86系統),bss段(Block Started by Symbol segment)通常是指用來存放程序中未初始化的全局變量的一塊內存區域,一般在初始化時bss 段部分將會清零。bss段屬於靜態內存分配,即程序一開始就將其清零了。
比如,在C語言之類的程序編譯完成之后,已初始化的全局變量保存在.data 段中,未初始化的全局變量保存在.bss 段中。
text和data段都在可執行文件中(在嵌入式系統里一般是固化在鏡像文件中),由系 統從可執行文件中加載;而bss段不在可執行文件中,由系統初始化。
各個段的關系
一個正在運行着的C編譯程序占用的內存分為代碼區、初始化數據區、未初始化數據區、堆區 和棧區5個部分。
(1)代碼區(text segment)。代碼區指令根據程序設計流程依次執行,對於順序指令,則只會執行一次(每個進程),如果反復,則需要使用跳轉指令,如果進行遞歸,則需 要借助棧來實現。
代碼區的指令中包括操作碼和要操作的對象(或對象地址引用)。如果是立即數(即具體的數值,如5),將直接包含在代碼中;如果是局部數據,將在棧區 分配空間,然后引用該數據地址;如果是BSS區和數據區,在代碼中同樣將引用該數據地址。
(2)全局初始化數據區/靜態數據區(Data Segment)。只初始化一次。
(3)未初始化數據區(BSS)。在運行時改變其值。
(4)棧區(stack)。由編譯器自動分配釋放,存放函數的參數值、局部變量的值等。其操作方式類似於數據結構中的棧。每當一個函數被調用,該函 數返回地址和一些關於調用的信息,比如某些寄存器的內容,被存儲到棧區。然后這個被調用的函數再為它的自動變量和臨時變量在棧區上分配空間,這就是C實現 函數遞歸調用的方法。每執行一次遞歸函數調用,一個新的棧框架就會被使用,這樣這個新實例棧里的變量就不會和該函數的另一個實例棧里面的變量混淆。
(5)堆區(heap)。用於動態內存分配。堆在內存中位於bss區和棧區之間。一般由程序員分配和釋放,若程序員不釋放,程序結束時有可能由OS 回收。
之所以分成這么多個區域,主要基於以下考慮:
一個進程在運行過程中,代碼是根據流程依次執行的,只需要訪問一次,當然跳轉和遞歸有可能使代碼執行多次,而數據一般都需要訪問多次,因此單獨開辟 空間以方便訪問和節約空間。
臨時數據及需要再次使用的代碼在運行時放入棧區中,生命周期短。
全局數據和靜態數據有可能在整個程序執行過程中都需要訪問,因此單獨存儲管理。
堆區由用戶自由分配,以便管理。
下面通過一段簡單的代碼來查看C程序執行時的內存分配情況。相關數據在運行時的位置如注釋所述。
//main.cpp
int a = 0; //a在全局已初始化數據區
char *p1; //p1在BSS區(未初始化全局變量)
main()
{
int b; //b在棧區
char s[] = "abc"; //s為數組變量,存儲在棧區,
//"abc"為字符串常量,存儲在已初始化數據區
char *p1,*p2; //p1、p2在棧區
char *p3 = "123456"; //123456\0在已初始化數據區,p3在棧區
static int c =0; //C為全局(靜態)數據,存在於已初始化數據區
//另外,靜態數據會自動初始化
p1 = (char *)malloc(10);//分配得來的10個字節的區域在堆區
p2 = (char *)malloc(20);//分配得來的20個字節的區域在堆區
free(p1);
free(p2);
}
