C語言記憶體分配

2021-08-07 05:52:56 字數 3718 閱讀 4919

可執行程式在儲存時(沒有調入到記憶體)分為**區(text)、資料區(data)和未初始化資料區(bss)3個部分。

(1)**區(text segment)。存放cpu執行的機器指令(machine instructions)。通常,**區是可共享的(即另外的執行程式可以呼叫它),因為對於頻繁被執行的程式,只需要在記憶體中有乙份**即可。**區通常是唯讀的,使其唯讀的原因是防止程式意外地修改了它的指令。另外,**區還規劃了區域性變數的相關資訊。(2)

全域性初始化資料區/靜態資料區(initialized data segment/data segment)。該區包含了在程式中明確被初始化的全域性變數、靜態變數(包括全域性靜態變數和區域性靜態變數)和常量資料(如字串常量)。

例如,乙個不在任何函式內的宣告(全域性資料):

int   maxcount = 99;

使得變數maxcount根據其初始值被儲存到初始化資料區中。

static mincount=100;

這宣告了乙個靜態資料,如果是在任何函式體外宣告,則表示其為乙個全域性靜態變數,如果在函式體內(區域性),則表示其為乙個區域性靜態變數。另外,如果在函式名前加上static,則表示此函式只能在當前檔案中被呼叫。

(3)未初始化資料區。亦稱bss區(uninitialized data segment),存入的是全域性未初始化變數。bss這個叫法是根據乙個早期的彙編運算子而來,這個彙編運算子標誌著乙個塊的開始。bss區的資料在程式開始執行之前被核心初始化為0或者空指標(null)。例如乙個不在任何函式內的宣告:

long  sum[1000];

將變數sum儲存到未初始化資料區。

圖所示為可執行**儲存時結構和執行時結構的對照圖。乙個正在執行著的c編譯程式占用的記憶體分為**區、初始化資料區、未初始化資料區、堆區和棧區5個部分。

(1)**區(text segment)。**區指令根據程式設計流程依次執行,對於順序指令,則只會執行一次(每個程序),如果反覆,則需要使用跳轉指令,如果進行遞迴,則需要借助棧來實現。

**區的指令中包括操作碼和要操作的物件(或物件位址引用)。如果是立即數(即具體的數值,如5),將直接包含在**中;如果是區域性資料,將在棧區分配空間,然後引用該資料位址;如果是bss區和資料區,在**中同樣將引用該資料位址。

(2)全域性初始化資料區/靜態資料區(data segment)。只初始化一次。

(3)未初始化資料區(bss)。在執行時改變其值。

(4)棧區(stack)。由編譯器自動分配釋放,存放函式的引數值、區域性變數的值等。其操作方式類似於

資料結構

中的棧。每當乙個函式被呼叫,該函式返回位址和一些關於呼叫的資訊,比如某些暫存器的內容,被儲存到棧區。然後這個被呼叫的函式再為它的自動變數和臨時變數在棧區上分配空間,這就是c實現函式遞迴呼叫的方法。每執行一次遞迴函式呼叫,乙個新的棧框架就會被使用,這樣這個新例項棧裡的變數就不會和該函式的另乙個例項棧裡面的變數混淆。

(5)堆區(heap)。用於動態記憶體分配。堆在記憶體中位於bss區和棧區之間。一般由程式設計師分配和釋放,若程式設計師不釋放,程式結束時有可能由os**。

之所以分成這麼多個區域,主要基於以下考慮:

乙個程序在執行過程中,**是根據流程依次執行的,只需要訪問一次,當然跳轉和遞迴有可能使**執行多次,而資料一般都需要訪問多次,因此單獨開闢空間以方便訪問和節約空間。

臨時資料及需要再次使用的**在執行時放入棧區中,生命周期短。

全域性資料和靜態資料有可能在整個程式執行過程中都需要訪問,因此單獨儲存管理。

堆區由使用者自由分配,以便管理。

下面通過一段簡單的**來檢視c程式執行時的記憶體分配情況。相關資料在執行時的位置如注釋所述

int a = 0;    //a在全域性已初始化資料區

char *p1;    //p1在bss區(未初始化全域性變數)

main()

在c語言中,物件可以使用靜態或動態的方式分配記憶體空間。

靜態分配:編譯器在處理程式源**時分配。

動態分配:程式在執行時呼叫malloc庫函式申請分配。

靜態記憶體分配是在程式執行之前進行的因而效率比較高,而動態記憶體分配則可以靈活的處理未知數目的。

靜態與動態記憶體分配的主要區別如下:

靜態物件是有名字的變數,可以直接對其進行操作;動態物件是沒有名字的變數,需要通過指標間接地對它進行操作。

靜態物件的分配與釋放由編譯器自動處理;動態物件的分配與釋放必須由程式設計師顯式地管理,它通過malloc()和free兩個函式(c++中為new和delete運算子)來完成。

以下是採用靜態分配方式的例子。

int a=100;

此行**指示編譯器分配足夠的儲存區以存放乙個整型值,該儲存區與名字a相關聯,並用數值100初始化該儲存區。

以下是採用動態分配方式的例子。

p1 = (char *)malloc(10*sizeof(int));//分配得來得10*4位元組的區域在堆區

此行**分配了10個int型別的物件,然後返回物件在記憶體中的位址,接著這個位址被用來初始化指標物件p1,對於動態分配的記憶體唯一的訪問方式是通過指標間接地訪問,其釋

放方法為:

free(p1);

3.1.2 棧和堆的區別

前面已經介紹過,棧是由編譯器在需要時分配的,不需要時自動清除的變數儲存區。裡面的變數通常是區域性變數、函式引數等。堆是由malloc()函式(c++語言為new運算子)分配的記憶體塊,記憶體釋放由程式設計師手動控制,在c語言為free函式完成(c++中為delete)。棧和堆的主要區別有以下幾點:

(1)管理方式不同。

棧編譯器自動管理,無需程式設計師手工控制;而堆空間的申請釋放工作由程式設計師控制,容易產生記憶體洩漏。

(2)空間大小不同。

棧是向低位址擴充套件的資料結構,是一塊連續的記憶體區域。這句話的意思是棧頂的位址和棧的最大容量是系統預先規定好的,當申請的空間超過棧的剩餘空間時,將提示溢位。因此,使用者能從棧獲得的空間較小。

堆是向高位址擴充套件的資料結構,是不連續的記憶體區域。因為系統是用鍊錶來儲存空閒記憶體位址的,且鍊錶的遍歷方向是由低位址向高位址。由此可見,堆獲得的空間較靈活,也較大。棧中元素都是一一對應的,不會存在乙個記憶體塊從棧中間彈出的情況。

(3)是否產生碎片。

對於堆來講,頻繁的malloc/free(new/delete)勢必會造成記憶體空間的不連續,從而造成大量的碎片,使程式效率降低(雖然程式在退出後

作業系統

會對記憶體進行**管理)。對於棧來講,則不會存在這個問題

。(4)增長方向不同。

堆的增長方向是向上的,即向著記憶體位址增加的方向;棧的增長方向是向下的,即向著記憶體位址減小的方向。

(5)分配方式不同。

堆都是程式中由malloc()函式動態申請分配並由free()函式釋放的;棧的分配和釋放是由編譯器完成的,棧的動態分配由alloca()函式完成,但是棧的動態分配和堆是不同的,他的動態分配是由編譯器進行申請和釋放的,無需手工實現。

(6)分配效率不同。

棧是機器系統提供的資料結構,計算機會在底層對棧提供支援:分配專門的暫存器存放棧的位址,壓棧出棧都有專門的指令執行。堆則是c函式庫提供的,它的機制很複雜,例如為了分配一塊記憶體,庫函式會按照一定的

演算法

(具體的演算法可以參考資料結構/作業系統)在堆記憶體中搜尋可用的足夠大的空間,如果沒有足夠大的空間(可能是由於記憶體碎片太多),就有需要作業系統來重新整理記憶體空間,這樣就有機會分到足夠大小的記憶體,然後返回。顯然,堆的效率比棧要低得多。

C語言記憶體分配

c語言的記憶體分配主要有5個區域 1 棧區 在執行函式時,函式內的區域性變數 不包括static變數 函式返回值的儲存單元在棧區上建立。函式執行結束時這些儲存單元自動被釋放。棧區記憶體分配運算內置於處理器的指令集中,效率很高,但分配的記憶體容量有限。2 堆區 程式在執行的時候用malloc call...

C語言記憶體分配

c語言的記憶體分配主要有5個區域 1 棧區 在執行函式時,函式內的區域性變數 不包括static變數 函式返回值的儲存單元在棧區上建立。函式執行結束時這些儲存單元自動被釋放。棧區記憶體分配運算內置於處理器的指令集中,效率很高,但分配的記憶體容量有限。2 堆區 程式在執行的時候用malloc call...

C語言記憶體分配

objective c從名字來看就可以知道是一門超c語言,所以了解c語言的記憶體模型對於理解objective c的記憶體管理有很大的幫助。c語言記憶體模型圖如下 從圖中可以看出記憶體被分成了5個區,每個區儲存的內容如下 棧區在什麼時候釋放記憶體呢?我們通過下面的乙個例子來說明下 void prin...