C語言--記憶體(說法二)
原始碼編譯之後,分為兩個狀態:儲存時、執行時。
儲存時
在linux環境下,可以對編譯好的二進位制檔案執行size
指令可以獲取該二進位制可執行檔案的結構情況:
# size test.out
程式碼區 | 全域性初始化資料區/靜態資料區 | 未初始化資料區 | 十進位制總合 | 十六進位制總合 | 檔名 |
---|---|---|---|---|---|
text | data | bss | dec | hex | filename |
未執行前,沒有調入到記憶體時,分為三個部分:程式碼區(text)、資料區(data)、未初始化資料區(bss)。
(1) 程式碼區(text)
存放CPU可執行的機器指令,由於程式被經常使用,防止其被意外修改,程式碼區通常是隻讀的。
(2) 全域性初始化資料區/靜態資料區(data)
存放被初始化的全域性變數、靜態變數(全域性靜態變數和區域性靜態變數)、常量資料(如字串常量)。
(3) 未初始化資料區(BSS)
存放未初始化的全域性變數,BSS這個叫法是根據早期的彙編運算子而來的,這個彙編運算子標誌著一個塊的開始。BSS區的資料在程式開始執行之前被核心初始化為0或空指標(NULL)。
執行時
一個正在執行的C程式,佔用的記憶體分為5個區域:程式碼區、初始化資料區/靜態資料區、未初始化資料區、堆區、棧區。
(1) 程式碼區(text)
程式碼區指令根據程式設計流程依次執行,對於順序指令,則只會執行一次,如果反覆,則需使用跳轉指令,如果進行遞迴,則需藉助棧來實現。
程式碼區包括操作碼和要操作的物件(或物件的地址引用),如果是立即數(即具體的數值,如2),將直接包含在程式碼中;如果是區域性資料,將在棧中分配空間,然後引用該資料的地址;如果是BSS區和資料區,在程式碼中同樣引用該資料的地址。
(2) 全域性初始化資料區/靜態資料區(data)
只初始化一次。上面已經說過,在程式編譯時,該區域已經被分配好了,這塊記憶體在程式的整個執行期間都存在,當程式結束時,才會被釋放。
(3)未初始化資料 區(BSS)
在執行時改變其值。
(4)棧區(stack)
存放函式的引數值和區域性變數,由編譯器自動分配釋放,其操作方式類似於資料結構的棧。其特點是不需要程式設計師去考慮記憶體管理的問題,很方便;同時棧的容量很有限,在Linux系統中,棧的容量只有8M,並且當相應的範圍結束時(如函式),區域性變數就不能再使用。
(5)堆區(heap)
有些操作物件只有在程式執行時才能確定,這樣編譯器在編譯時就無法為他們預先分配空間,只有程式執行時才分配,這就是動態記憶體分配。堆區就是用於動態記憶體分配(如malloc的動態記憶體分配),堆在記憶體中位於bss區和棧區之間,一般由程式設計師申請和釋放。
之所以分配如此多的區域,主要是因為:
一個程序在執行時,程式碼是根據流程依次執行的,程式碼只需訪問一次,當然跳轉或遞迴時程式碼會被執行多次,而資料一般都需要訪問多次,因此單獨開闢空間以便訪問和節約空間。
int a = 0; //全域性初始化區
char *p1; //全域性未初始化區
int main()
{
int b; // 棧
char s[] = "abc"; //棧
char *p2; //棧
char *p3 = "123456"; //123456\0在常量區,而p3在棧上
static int c =0; //全域性(靜態)初始化區
p1 = (char *)malloc(10);
p2 = (char *)malloc(20); //分配得來得10和20位元組的區域就在堆區
strcpy(p1, "123456"); //123456\0放在常量區,編譯器可能會將它與p3所指向的"123456"優化成一個地方。
return 0;
}