乙個由 c/c++編譯的程式占用的記憶體(memory)分為以下幾個部分:
1. 程式**區(.text)
(即**段-text)存放函式體的二進位制**,**段由程式中執行的機器**組成。在c語言中,程式語句進行編譯後,形成機器**。在執行程式的過程中,cpu的程式計數器指向**段的每一條機器**,並由處理器依次執行。
2. 文字常量區(.rodata)
常量字串就是放在這裡的(即唯讀資料段),程式結束後由系統
釋放(rodata—read only data)。唯讀資料段是程式使用的一些不會被更改的資料,使用這些資料的方式類似查表式的操作,由於這些變數不需要更改,因此只需要放置在唯讀儲存器中即可。
3. 全域性區/靜態區(static)
全域性變數和靜態變數的儲存是放在一塊的。初始化的全域性變數和靜態變數在一塊區域(.rwdata or .data)(
即已初始化讀寫資料段
),未初始化的全域性變數和未初始化的靜態變數在相鄰的另一塊區域(.bss)(
即未初始化資料段
), 程式結束後由系統釋放。已初始化資料是在程式中宣告,並且具有初值的變數,這些變數需要占用儲存器的空間,在程式執行時它們需要位於可讀寫的記憶體區域內,並具有初值,以供程式執行時讀寫。未初始化資料是在程式中宣告,但是沒有初始化的變數,這些變數在程式執行之前不需要占用儲存器的空間。(注意:在 c++中,已經不再嚴格區分bss和
data了,它們共享一塊記憶體區域。)
4. 堆區(heap)
一般由程式設計師分配釋放(new/malloc/calloc delete/free),若程式設計師不釋放,程式結束時可能由作業系統**。(注意:它與資料結構中的堆是兩回事,但分配方式倒類似於鍊錶。)
5. 棧區(stack)
由編譯器自動分配釋放,存放函式的引數值,區域性變數的值等。其操作方式類似於資料結構中的棧。
靜態區與動態區、映像與執行時、節與段:
1. **段(text)、唯讀資料段(ro data)、讀寫資料段(rw data)、未初始化資料段(bss)屬於靜態區域,而堆(heap)和棧(stack)屬於動態區域。
2. **段(程式**區)、唯讀資料段和(文字常量區)、讀寫資料段(全域性區/靜態區中已初始化的)將在鏈結之後產生,未初始化資料段(全域性區/靜態區中未初始化的)將在程式初始化的時候開闢,而堆和棧將在程式的執行中分配和釋放。
3. c語言程式分為映像和執行時兩種狀態。在編譯-連線後形成的映像中,將只包含**段(text)、唯讀資料段(ro data)和讀寫資料段(rw data)。在程式執行之前,將動態生成未初始化資料段(bss),在程式的執行時還將動態形成堆(heap)區域和棧(stack)區域。一般來說,在靜態的映像檔案中,各個部分稱之為節(section),而在執行時的各個部分稱之為段(segment)。如果不詳細區分,可以統稱為段。
在c語言的程式中,對變數的使用還有以下幾點需要注意:
1. 函式體中定義的變數通常是在棧上,不需要在程式中進行管理,由編繹器處理。
2. 用malloc,calloc,realloc等分配記憶體的函式所分配的記憶體空間在堆上,程式必須保證在使用free釋放,否則會發生記憶體洩漏。
3. 所有函式體外定義的是全域性變數,加了static後的變數不管是在函式內部或外部都放在全域性區。
4. 使用const定義的變數將放於程式的唯讀資料區。
棧空間主要用於以下3種資料的儲存:
1.函式內部的動態變數
2.函式的引數
3.函式的返回值
棧空間是動態開闢與**的。在函式呼叫過程中,如果函式呼叫的層次比較多,所需要的棧空間也逐漸加大,對於引數的傳遞和返回值,如果使用較大的結構體,在使用的棧空間也會比較大。
例子程式:
//堆和棧的區別:1.管理方式main.cpp
int a = 0; //
全域性初始化區(data)
char *p1; //
全域性未初始化區(bss)
intmain()
對於棧來講,是由編譯器自動管理;對於堆來說,釋放工作由程式設計師控制,容易產生
memory leak。
2. 空間大小
一般來講在 32 位系統下,堆記憶體可以達到接近 4g 的空間,從這個角度來看堆記憶體幾乎是沒有什麼限制的。但是對於棧來講,一般都是有一定的空間大小的,例如,在 vc6 下面,預設的棧空間大小大約是 1m。
3. 碎片問題
對於堆來講,頻繁的new/delete 勢必會造成記憶體空間的不連續,從而造成大量碎片,使程式效率降低;對於棧來講,則不會存在這個問題,因為棧是先進後出的佇列,永遠都不可能有乙個記憶體塊從棧中間彈出。
4. 生長方向
對於堆來講,生長方向是向上的,也就是向著記憶體位址增加的方向;對於棧來講,它的生長方向是向下的,是向著記憶體位址減小的方向增長。
5. 分配方式
堆都是動態分配的,沒有靜態分配的堆;棧有 2 種分配方式:靜態分配和動態分配。靜態分配是編譯器完成的,比如區域性變數的分配,動態分配由 alloca 函式進行分配,但是棧的動態分配和堆是不同的,它的動態分配是由編譯器進行釋放,不需要我們手工實現。
6. 分配效率
棧是機器系統提供的資料結構,計算機會在底層分配專門的暫存器存放棧的位址,壓棧出棧都有專門的指令執行,這就決定了棧的效率比較高;堆則是 c/c++函式庫提供的,它的機制是很複雜的,例如為了分配一塊記憶體,庫函式會按照一定的演算法(具體的演算法可以參考資料結構/作業系統)在堆記憶體中搜尋可用的足夠大小的空間,如果沒有足夠大小的空間(可能是由於記憶體碎片太多),就有可能呼叫系統功能去增加程式資料段的記憶體空間,然後進行返回。顯然,堆的效率比棧要低得多。
無論是堆還是棧,都要防止越界現象的發生。
關於 global 和 static 型別的一點討論
1. static 全域性變數與普通的全域性變數有什麼區別 ?
全域性變數(外部變數)的定義之前再冠以 static 就構成了靜態的全域性變數。全域性變數本身就是靜態儲存方式, 靜態全域性變數當然也是靜態儲存方式。 這兩者在儲存方式上並無不同。
這兩者的區別在於非靜態全域性變數的作用域是整個源程式, 當乙個源程式由多個原始檔組成時,非靜態的全域性變數在各個原始檔中都是有效的。而靜態全域性變數則限制了其作用域,即只在定義該變數的原始檔內有效,在同一源程式的其它原始檔中不能使用它。
由於靜態全域性變數的作用域侷限於乙個原始檔內,只能為該原始檔內的函式公用,因此可以避免在其它原始檔中引起錯誤。static 全域性變數只初使化一次,防止在其他檔案單元中被引用。
2. static 區域性變數和普通區域性變數有什麼區別 ?
把區域性變數改變為靜態變數後是改變了它的儲存方式即改變了它的生存期。把全域性變數改變為靜態變數後是改變了它的作用域,限制了它的使用範圍。static 區域性變數只被初始化一次,下一次依據上一次結果值。
3. static 函式與普通函式有什麼區別?
static 函式與普通函式作用域不同,僅在本檔案。只在當前原始檔中使用的函式應該說明為內部函式(static),內部函式應該在當前原始檔中說明和定義。對於可在當前原始檔以外使用的函式,應該在乙個標頭檔案中說明,要使用這些函式的原始檔要包含這個標頭檔案.static 函式在記憶體中只有乙份(.data),普通函式在每個被呼叫中維持乙份拷貝。
整合自兩篇: 和
C程式儲存布局 棧
所有的自動變數以及函式呼叫時所需要儲存的資訊 返回位址 函式呼叫前各暫存器的值等 都儲存在棧上。每次呼叫函式時,棧會隨著函式的呼叫而生長,隨著函式呼叫結束而消亡。自動變數有3種儲存方式 一是儲存在資料段或者bss段 靜態區域性變數 一是儲存在暫存器裡 暫存器變數 一是儲存在棧中 一般自動變數 由於絕...
c程式儲存空間布局
摘自 c程式一直由下面幾部分組成 1 棧 由編譯器自動分配釋放管理。區域性變數及每次函式呼叫時返回位址 以及呼叫者的環境資訊 例如某些機器暫存器 都存放在棧中。新被呼叫的函式在棧上為其自動和臨時變數分配儲存空間。通過以這種方式使用棧,c函式可以遞迴 呼叫。遞迴函式每次呼叫自身時,就使用乙個新的棧幀,...
c程式儲存空間布局
摘自 c程式一直由下面幾部分組成 1 棧 由編譯器自動分配釋放管理。區域性變數及每次函式呼叫時返回位址 以及呼叫者的環境資訊 例如某些機器暫存器 都存放在棧中。新被呼叫的函式在棧上為其自動和臨時變數分配儲存空間。通過以這種方式使用棧,c函式可以遞迴 呼叫。遞迴函式每次呼叫自身時,就使用乙個新的棧幀,...