3.1.1 c程式記憶體分配
1.c 程式結構
下面列出 c 語言可執行程式的基本情況(linux 2.6 環境/gcc4.0)。
[root@localhost ctest]# ls test -l //test 為乙個可執行程式
-rwxr-xr-x 1 root root 4868 mar 26 08:10 test
[root@localhost ctest]# file test //此檔案基本情況
test: elf 32-bit lsb executable, intel 80386, version 1 (sysv), for gnu/linux 2.2.5,
dynamically linked (uses shared libs), not stripped
[root@localhost ctest]# size test //此二進位制可執行檔案結構情況
//**區 靜態資料/全域性初始化資料區 未初始化資料區 十進位制總和 十六進製制總和 檔名
text data bss dec hex filename
906 284 4 1194 4aa test
可以看出,此可執行程式在存 儲時(沒有調入到記憶體)分為**區(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儲存到未初始化資料區。
圖 3-1 所示為可執行**儲存時結構和執行時結構的對照圖。乙個正在執行著的 c 編譯程式
占用的記憶體分為**區、初始化資料區、未初始化資料區、堆區和 棧區 5 個部分。
可執行**
(**區)
已初始化全域性變數、靜態變數
和常量資料(資料區)
堆區(向上增長)
棧區(向 下增長)
未初始化全域性變數
(bss區,用零初始化)
可執行**
(**區)
已初始化全域性變數、靜態變數
和 常量資料(資料區)
未初始化變數
(bss區,用零初始化)
高位址低位址
儲存時三個區域
(用size查 看)
執行時的五
個區域儲存時的 3 個區域
(用 size 檢視)
執行時的 5 個區域
圖 3-1 c 程式的記憶體布局
(1)**區(text segment)。**區指令根據程式設計流程依次執行,對於順序指令,
則 只會執行一次(每個程序),如果反覆,則需要使用跳轉指令,如果進行遞迴,則需要借助
棧來實現。
**區的指令中包括操作碼和要操作的對 象(或物件位址引用)。如果是立即數(即具體
的數值,如 5),將直接包含在**中;如果是區域性資料,將在棧區分配空間,然後引用該數
據 位址;如果是 bss 區和資料區,在**中同樣將引用該資料位址。
(2)全域性初始化資料區/靜態資料區(data segment)。只初始化一次。
(3)未初始化資料區(bss)。在執行時改變其值。
(4) 棧區(stack)。由編譯器自動分配釋放,存放函式的引數值、區域性變數的值等。其
操作方式類似於資料結構中的棧。每當乙個函式被呼叫,該函式返 回位址和一些關於呼叫的
資訊,比如某些暫存器的內容,被儲存到棧區。然後這個被呼叫的函式再為它的自動變數和
臨時變數在棧區上分配空間, 這就是 c 實現函式遞迴呼叫的方法。每執行一次遞迴函式呼叫,
乙個新的棧框架就會被使用,這樣這個新例項棧裡的變數就不會和該函式的另乙個例項 棧裡
面的變數混淆。
(5)堆區(heap)。用於動態記憶體分配。堆在記憶體中位於 bss 區和棧區之間。一般由程式
員分配和釋 放,若程式設計師不釋放,程式結束時有可能由 os **。
之所以分成這麼多個區域,主要基於以下考慮:
l 乙個程序在執行過程中,**是根據流程依次執行的,只需要訪問一次,當然跳轉
和遞迴有可能使**執行多次,而資料一般都需要訪問多次,因此單獨開 闢空間以
方便訪問和節約空間。
l 臨時資料及需要再次使用的**在執行時放入棧區中,生命周期短。
l 全域性資料和靜態資料有可能在整個程式執行過程中都需要訪問,因此單獨儲存管理。
l 堆區由使用者自由分配,以便管理。
下面通過一段簡單 的**來檢視 c 程式執行時的記憶體分配情況。相關資料在執行時的位
置如注釋所述。 //main.cpp
int a = 0; //a 在全域性已初始化資料區
char *p1; //p1 在 bss 區(未初始化全域性變數)
main()
2.記憶體分配方式
在 c 語言中,物件可以使用靜態或動態的方式分配記憶體空間。
l 靜態分配:編譯器在處理程式源**時分配。
l 動態分配:程式在執行時呼叫 malloc 庫函式申請分配。
靜態記憶體分配是在程式執行之前進行的因而效率比較高,而動態記憶體分配則可以靈活的
處理未知數目的。
靜 態與動態記憶體分配的主要區別如下:
l 靜態物件是有名字的變數,可以直接對其進行操作;動態物件是沒有名字的變數,
需要通過指標間接地 對它進行操作。
l 靜態物件的分配與釋放由編譯器自動處理;動態物件的分配與釋放必須由程式設計師
顯式地管理,它通過 malloc()和 free 兩個函式(c++中為 new 和 delete 運算子)來
完成。
以下是採用靜態分配方式的例子。
int a=100;
此行**指示編譯器分配足夠的儲存區以存放乙個整型值,該儲存區與名字 a 相關聯,
並用數值 100 初始化該儲存區。
以下是採用動態分配方式的例子。
p1 = (char *)malloc(10*sizeof(int)); //分配得來得 10*4 位元組的區域在堆區
此行**分配了 10 個 int 型別的物件,然後返回物件在記憶體中的位址,接著這個位址被
用來初始化指 針對象 p1,對於動態分配的記憶體唯一的訪問方式是通過指標間接地訪問,其釋
放方法為:
free(p1);
c 程式記憶體分配
乙個由c c 編譯的程式占用的記憶體分為以下幾個部分 1 棧區 stack 由編譯器自動分配釋放 存放函式的引數值,區域性變數的值等。其操作方式類似於資料結構中的棧。2 堆區 heap 一般由程式設計師分配釋放,若程式設計師不釋放,程式結束時可能由os 注意它與資料結構中的堆是兩回事,分配方式倒是類...
C程式記憶體分配
在任何程式設計環境及語言中,記憶體管理都十分重要。在目前的計算機系統或嵌入式系統中,記憶體資源仍然是有限的。因此在程式設計中,有效地管理記憶體資源是程式設計師首先考慮的問題。第1節主要介紹記憶體管理基本概念,重點介紹c程式中記憶體的分配,以及c語言編譯後的可執行程式的儲存結構和執行結構,同時還介紹了...
C程式記憶體分配
從作業系統的角度簡單介紹一下程序。程序是占有資源的最小單位,這個資源當然包括記憶體。在現代作業系統中,每個程序所能訪問的記憶體是互相獨立的 一些交換區除外 而程序中的執行緒所以共享程序所分配的記憶體空間。在作業系統的角度來看,程序 程式 資料 pcb 程序控制塊 區 text 用來存放cpu執行的機...