C語言編程程序的(de)内存如何布局
重點關注以下(xià)内容:
C語言程序在内存中各個(gè)段的(de)組成
C語言程序連接過程中的(de)特性和(hé)常見錯誤
C語言程序的(de)運行方式
一:C語言程序的(de)存儲區(qū)域
由C語言代碼(文本文件)形成可(kě)執行程序(二進制文件),需要經過編譯-彙編-連接三個(gè)階段。編譯過程把C語言文本文件生成彙編程序,彙編過程把彙編程序形成二進制機器代碼,連接過程則将各個(gè)源文件生成的(de)二進制機器代碼文件組合成一個(gè)文件。
C語言編寫的(de)程序經過編譯-連接後,将形成一個(gè)統一文件,它由幾個(gè)部分(fēn)組成。在程序運行時(shí)又會産生其他(tā)幾個(gè)部分(fēn),各個(gè)部分(fēn)代表了(le)不同的(de)存儲區(qū)域:
1.代碼段(Code或Text)
代碼段由程序中執行的(de)機器代碼組成。在C語言中,程序語句進行編譯後,形成機器代碼。在執行程序的(de)過程中,CPU的(de)程序計數器指向代碼段的(de)每一條機器代碼,并由處理(lǐ)器依次運行。
2.隻讀數據段(RO data)
隻讀數據段是程序使用(yòng)的(de)一些不會被更改的(de)數據,使用(yòng)這(zhè)些數據的(de)方式類似查表式的(de)操作,由于這(zhè)些變量不需要更改,因此隻需要放置在隻讀存儲器中即可(kě)。
3.已初始化(huà)讀寫數據段(RW data)
已初始化(huà)數據是在程序中聲明(míng),并且具有初值的(de)變量,這(zhè)些變量需要占用(yòng)存儲器的(de)空間,在程序執行時(shí)它們需要位于可(kě)讀寫的(de)内存區(qū)域内,并具有初值,以供程序運行時(shí)讀寫。
4.未初始化(huà)數據段(BSS)
未初始化(huà)數據是在程序中聲明(míng),但是沒有初始化(huà)的(de)變量,這(zhè)些變量在程序運行之前不需要占用(yòng)存儲器的(de)空間。
5.堆(heap)
堆内存隻在程序運行時(shí)出現,一般由程序員(yuán)分(fēn)配和(hé)釋放。在具有操作系統的(de)情況下(xià),如果程序沒有釋放,操作系統可(kě)能在程序(例如一個(gè)進程)結束後回收内存。
6.棧(stack)
棧内存隻在程序運行時(shí)出現,在函數内部使用(yòng)的(de)變量、函數的(de)參數以及返回值将使用(yòng)棧空間,棧空間由編譯器自動分(fēn)配和(hé)釋放。
C語言目标文件的(de)内存布局
看一個(gè)例子:
int a = 0; //全局初始化(huà)區(qū),。data段
static int b=20; //全局初始化(huà)區(qū),。data段
char *p1; //全局未初始化(huà)區(qū) .bss段
const int A = 10; //.rodata段
void main(void)
{
int b; //棧
char s[] = "abc"; //棧
char *p2; //棧
static int c = 0; //全局(靜态)初始化(huà)區(qū) .data段
char *p3 = "123456"; //123456\0在常量區(qū),p3 在棧上。
p1 = (char*) malloc(10);//分(fēn)配得(de)來(lái)的(de)10和(hé)20個(gè)字節的(de)區(qū)域就在堆區(qū)
p2 = (char*) malloc(20);
strcpy(p1, "123456"); //123456\0 在常量區(qū),編譯器可(kě)能會将它與p3所指向的(de)"123456"優化(huà)成一個(gè)地方
}
代碼段、隻讀數據段、讀寫數據段、未初始化(huà)數據段屬于靜态區(qū)域,而堆和(hé)棧屬于動态區(qū)域。代碼段、隻讀數據段和(hé)讀寫數據段将在鏈接之後産生,未初始化(huà)數據段将在程序初始化(huà)的(de)時(shí)候開辟,而堆和(hé)棧将在程序的(de)運行中分(fēn)配和(hé)釋放。C語言程序分(fēn)爲映像和(hé)運行時(shí)兩種狀态。在編譯-連接後形成的(de)映像中,将隻包含代碼段(Text)、隻讀數據段(RO Data)和(hé)讀寫數據段(RW Data)。在程序運行之前,将動态生成未初始化(huà)數據段(BSS),在程序的(de)運行時(shí)還(hái)将動态形成堆(Heap)區(qū)域和(hé)棧(Stack)區(qū)域。一般來(lái)說,在靜态的(de)映像文件中,各個(gè)部分(fēn)稱之爲節(Section),而在運行時(shí)的(de)各個(gè)部分(fēn)稱之爲段(Segment)。如果不詳細區(qū)分(fēn),可(kě)以統稱爲段。
知識點:
C語言在編譯和(hé)連接後,将生成代碼段(Text)、隻讀數據段(RO Data)和(hé)讀寫數據段(RW Data)。在運行時(shí),除了(le)以上三個(gè)區(qū)域外,還(hái)包括未初始化(huà)數據段(BSS)區(qū)域和(hé)堆(Heap)區(qū)域和(hé)棧(Stack)區(qū)域。
二:C語言程序的(de)段
1.代碼段(code或text)
代碼段由各個(gè)函數産生,函數的(de)每一個(gè)語句将最終經過編繹和(hé)彙編生成二進制機器代碼(具體生生哪種體系結構的(de)機器代碼由編譯器決定)。
2.隻讀數據段(RO Data)
隻讀數據段由程序中所使用(yòng)的(de)數據産生,該部分(fēn)數據的(de)特點是在運行中不需要改變,因此編譯器會将該數據段放入隻讀的(de)部分(fēn)中。C語言中的(de)隻讀全局變量,隻讀局部變量,程序中使用(yòng)的(de)常量等會在編譯時(shí)被放入到隻讀數據區(qū)。
注意:定義全局變量const char a[100]={"ABCDEFG"};将生成大(dà)小爲100個(gè)字節的(de)隻讀數據區(qū),并使用(yòng)“ABCDEFG”初始化(huà)。如果定義爲:const char a[ ]={"ABCDEFG"};則根據字符串長(cháng)度生成8個(gè)字節的(de)隻讀數據段(還(hái)有’\0’),所以在隻讀數據段中,一般都需要做(zuò)完全的(de)初始化(huà)。
3.讀寫數據段(RW Data)
讀寫數據段表示了(le)在目标文件中一部分(fēn)可(kě)以讀也(yě)可(kě)以寫的(de)數據區(qū),在某些場(chǎng)合它們又被稱爲已初始化(huà)數據段,這(zhè)部分(fēn)數據段和(hé)代碼段,與隻讀數據段一樣都屬于程序中的(de)靜态區(qū)域,但具有可(kě)寫性的(de)特點。通(tōng)常已初始化(huà)的(de)全局變量和(hé)局部靜态變量被放在了(le)讀寫數據段,如: 在函數中定義static char b[ 100]={“ABCDEFG”};讀寫數據區(qū)的(de)特點是必須在程序經過初始化(huà),如果隻定義,沒初始值,則不會生成讀寫數據區(qū),而會定位爲未初始化(huà)數據區(qū)(BSS)。如果全局變量(函數外部定義的(de)變量)加入static修飾,這(zhè)表示隻能在文件内使用(yòng),而不能被其他(tā)文件使用(yòng)。
4. 未初始化(huà)數據段(BSS)
與讀寫數據段類似,它也(yě)屬于靜态數據區(qū),但是該段中的(de)數據沒有經過初始化(huà)。因此它隻會在目标文件中被标識,而不會真正稱爲目标文件中的(de)一段,該段将會在運行時(shí)産生。未初始化(huà)數據段隻在運行的(de)初始化(huà)階段才會産生,因此它的(de)大(dà)小不會影(yǐng)響目标文件的(de)大(dà)小。
在C語言的(de)程序中,對(duì)變量的(de)使用(yòng)還(hái)有以下(xià)幾點需要注意:
1.函數體中定義的(de)變量通(tōng)常是在棧上,不需要在程序中進行管理(lǐ),由編繹器處理(lǐ)。
2.用(yòng)malloc,calloc,realloc等分(fēn)配内存的(de)函數所分(fēn)配的(de)内存空間在堆上,程序必須保證在使用(yòng)free釋放,否則會發生内存洩漏。
3.所有函數體外定義的(de)是全局變量,加了(le)static後的(de)變量不管是在函數内部或外部都放在全局區(qū)。
4.使用(yòng)const定義的(de)變量将放于程序的(de)隻讀數據區(qū)。
三:程序中段的(de)使用(yòng)
下(xià)面用(yòng)一個(gè)簡單的(de)例子來(lái)說明(míng)C語言中變量和(hé)段的(de)對(duì)應關系。C語言程序中的(de)全局區(qū)(靜态區(qū)),實際對(duì)應著(zhe)下(xià)述幾個(gè)段:RO Data; RW Data ; BSS Data.
一般來(lái)說,直接定義的(de)全局變量在未初始化(huà)數據區(qū),如果該變量有初始化(huà)則是在已初始化(huà)數據區(qū)(RW Data),加上const則将放在隻讀數據區(qū)。
const char ro[ ] = {"this is read only data"}; //隻讀數據區(qū)
static char rw_1[ ] ={"this is global read write data"}; //已初始化(huà)讀寫數據段
char BSS_1[ 100]; //未初始化(huà)數據段
const char *ptrconst ="constant data"; //字符串放在隻讀取數據段
int main()
{
short b; //在棧上,占用(yòng)2個(gè)字節
char a[100]; //在棧上開辟100個(gè)字節, 它的(de)值是其首地址
char s[ ]="abcdefg"; //s在棧上,占用(yòng)4個(gè)字節,"abcdefg"本身放置在隻讀數據存儲區(qū),占8個(gè)字節
char *p1; //p1在棧上,占用(yòng)4個(gè)字節
char *p2="123456"; //p2 在棧上,p2指向的(de)内容不能改,“123456”在隻讀數據區(qū)
static char rw_2[ ]={"this is local read write data"};//局部已初始化(huà)讀寫數據段
static char BSS_2[100]; //局部未初始化(huà)數據段
static int c = 0; //全局(靜态)初始化(huà)區(qū)
p1=(char *)malloc(10 * sizeof(char ) ); //分(fēn)配内存區(qū)域在堆區(qū)
strcpy(p1,"xxxx"); //“XXXX”放在隻讀數據區(qū),占5個(gè)字節
free(p1); //使用(yòng)free釋放p1所指向的(de)内存
return 0;
}
讀寫數據段包含了(le)憶初始化(huà)的(de)全局變量 static char rw_1[ ]以及局部靜态變量static rw_2[ ].其差别在于編繹時(shí),是在函數内部使用(yòng)的(de)還(hái)是可(kě)以在整個(gè)文件中使用(yòng)。對(duì)于rw_1[] 無論有無static 修飾,其都将被放置在讀寫數據區(qū),隻是能否被其它文件引用(yòng)與否。對(duì)于後者就不一樣了(le),它是局部靜态變量,放置在讀寫數據區(qū),如果沒static修飾,其意義完全改變,它将會是開辟在棧空間的(de)局部變量,而不是靜态變量,在這(zhè)裏rw_1[],rw_2[]後沒具體數值,表示靜态區(qū)大(dà)小同後面字符串長(cháng)度決定。
對(duì)于未初始化(huà)數據區(qū)BSS_1[100]與BSS_2[100],其區(qū)别在于前者是全局變量,在所有文件中都可(kě)以使用(yòng);後者是局部變量,隻在函數内部使用(yòng)。未初始化(huà)數據段不設置後面的(de)初始化(huà)數值,因此必須使用(yòng)數值指定區(qū)域的(de)大(dà)小,編繹器将根據大(dà)小設置BSS中需要增加的(de)長(cháng)度。
棧空間主要用(yòng)于以下(xià)3數據的(de)存儲:
1.函數内部的(de)動态變量
2.函數的(de)參數
3.函數的(de)返回值
- 全部評論(0)