C語言編程程序的內存怎樣布局
在c語言中,每一個變量和函數有兩個屬性:
數據類型
和數據的存儲類別
。
C語言中局部變量和全局變量變量的存儲類別(static
,extern
,auto
,register
)
1. 從變量的作用域劃分變量(即從空間
)角度來分
1.全局變量
2.局部變量
2. 從變量值存在的時間或存儲類別(即生存期
)角度來分
2.1. 靜態存儲區
存放下面數據:
代碼段(text)
、僅僅讀數據段(rodata)
、讀寫數據段(rwdata)
、未初始化數據段(bbs)
靜態存儲區存放所有的全局變量, 這些變量將在鏈接之後產生, 程序執行完成就釋放, 程序執行的過程中它們占領固定的存儲單元, 而不會動態的進行分配和釋放
2.2. 動態存儲區
存放下面數據:
函數形參
、自己主動變量(未加static聲明的局部變量)
、函數調用時的現場保護和返回地址
對以上這些數據,在函數開始調用時分配動態存儲空間。函數結束時釋放這些空間。
3. 從用戶內存空間角度分為三個部分
1. 程序區
一行一行的等待執行的機器碼
2. 靜態存儲區
3. 動態存儲區
4. 從C程序執行時
又可分為下面存儲區
1. 代碼段 (Code | Text)
代碼段由程序中執行的機器代碼組成。
在C語言中。程序語句進行編譯後,形成機器代碼。在執行程序的過程中,CPU的程序計數器指向代碼段的每一條
機器代碼
。並由處理器依次執行。
2. 僅僅讀數據段(ROData)
2.1 ROData介紹
- 僅僅讀數據段是程序使用的一些不會被更改的數據,使用這些數據的方式相似查表式的操作。因為這些變量不須要更改,因此僅僅須要放置在僅僅讀存儲器中就可以。
- 僅僅讀數據段由程序中所使用的數據產生,該部分數據的特點是在執行中不須要改變,因此編譯器會將該數據段放入僅僅讀的部分中。C語言中的
僅僅讀全局變量
,僅僅讀局部變量
。程序中使用的常量
等會在編譯時
被放入
到僅僅讀數據區
。註意
:定義全局變量const char a[100]={“ABCDEFG”};將生成大小為100個字節的僅僅讀數據區,並使用“ABCDEFG”初始化。假設定義為:const char a[ ]={“ABCDEFG”};則依據字符串長度生成8個字節的僅僅讀數據段(還有’\0’),
所以在僅僅讀數據段中,一般都須要做全然的初始化。
2.2 Example
#define A=18; ##常量
const int A = 18; ##僅僅讀全局變量
int main(){
const int B = 18; ##僅僅讀局部變量
}
3. 已初始化讀寫數據段(RW data)
3.1 RWData介紹
- 已初始化數據是在程序中聲明,而且具有初值的變量。這些變量須要占用存儲器的空間。在程序執行時它們須要位於可讀寫的內存區域內。並具有初值。以供程序執行時讀寫。
- 全局變量所有存放在靜態存儲區,
在程序開始執行時給全局變量分配存儲區
。程序行完成就釋放
。在程序執行過程中它們占領固定的存儲單元
,而不動態地進行分配和釋放
;全局變量
靜態(static) 局部變量
3.2 Example
int global_init_val=1; ## 全局變量
int main(int argc, char * argv[]){
static int a=1; ## 靜態(static) 局部變量
}
4. 未初始化數據段(BSS)
4.1 BSS介紹
未初始化數據是在程序中聲明,可是沒有初始化的變量,這些變量在程序執行之前不須要占用存儲器的空間。
4.1 Example
int global_noinit_val; ## 全局未初始化全局變量
char *p1; ## 全局未初始化全局變量
int main(int argc, char * argv[]){
......
}
5. 堆(heap)
5.1 堆空間介紹
堆內存
僅僅在程序執行時出現
。一般由程序猿分配
和釋放
。
在具有操作系統的情況下,假設程序沒有釋放
,操作系統
可能在程序
(比如一個進程
)結束後回收內存
。
5.2 Example
p1 = (char*) malloc(10); ## 分配得來的10和20個字節的區域就在堆區
p2 = (char*) malloc(20);
6. 棧(stack)
6.1 棧空間介紹
- 棧內存
僅僅在程序執行時出現
。在函數內部使用的變量
、函數的參數
以及返回值
將使用棧
空間,- 棧空間由編譯器自己主動分配和釋放。
棧空間
是動態開辟
與回收
的。在函數調用過程中,假設函數調用的層次比較多
,所須要的棧空間也逐漸加大
- 對於
參數的傳遞
和返回值
,假設使用較大的結構體
,在使用的棧空間也會比較大
。
6.2 棧區主要用於下面數據的存儲
- 函數內部的動態變量
- 函數的參數
- 函數的返回值
6.3 Example
void main(void){
int b; ## 棧
char s[] = "abc"; ## 棧
char *p2; ## 棧
char *p3 = "123456"; ## 123456\0在常量區 ## p3 在棧上。
}
===================================華麗的切割線=========================
5. 4種局部變量和全局變量的存儲類別(static
, extern
, auto
, register
)
5.1 Static
有時希望函數中的局部變量的值在函數調用結束後不消失而保留原值,這時就應該指定局部變量為“靜態局部變量”,用關鍵字static進行聲明。
int f(int a)
{
auto int b=0;
static int c=3;
b=b+1;
c=c+1;
return(a+b+c);
}
int main(void)
{
int a=2,i;
for(i=0;i<3;i++)
printf("%d",f(a));
}
對靜態局部變量的說明:
1)靜態局部變量屬於靜態存儲類別,在靜態存儲區內分配存儲單元。在程序整個執行期間都不釋放。而自己主動變量(即動態局部變量)屬於動態存儲類別。占動態存儲空間,函數調用結束後即釋放。
2)靜態局部變量在編譯時賦初值,即僅僅賦初值一次;而對自己主動變量賦初值是在函數調用時進行,每調用一次函數又一次給一次初值。相當於執行一次賦值語句。
3)假設在定義局部變量時不賦初值的話,則對靜態局部變量來說,編譯時自己主動賦初值0(對數值型變量)或空字符(對字符變量)。而對自己主動變量來說,假設不賦初值則它的值是一個不確定的值。
5.2 Extern
外部變量
(即全局變量
)是在函數的外部定義的。它的作用域為從變量定義處開始,到本程序文件的末尾。假設外部變量不在文件的開頭定義,其有效的作用範圍僅僅限於定義處到文件終了。假設在定義點之前的函數想引用該外部變量,則應該在引用之前用關鍵字extern
對該變量作“外部變量聲明
”。
表示該變量是一個已經定義的外部變量
。有了此聲明。就能夠從“聲明
”處起。合法地使用該外部變量
。
int max(int x,int y)
{
int z;
z=x>y?x:y;
return(z);
}
int main(void)
{
extern A,B;
printf("%d\n",max(A,B));
}
int A=13,B=-8;
說明:
在本程序文件的最後1行定義了外部變量A。B,但因為外部變量定義的位置在函數main之後,因此本來在main函數中不能引用外部變量A,B。如今我們在main函數中用extern對A和B進行“外部變量聲明”,就能夠從“聲明”處起,合法地使用該外部變量A和B。
5.3 Auto
函數中的局部變量。如不專門聲明為static存儲類別,都是動態地分配存儲空間的。數據存儲在動態存儲區中
。函數中的形參和在函數中定義的變量(包括在復合語句中定義的變量),都屬此類,在調用該函數時系統會給它們分配存儲空間,在函數調用結束時就自己主動釋放這些存儲空間。這類局部變量稱為
自己主動變量
。自己主動變量用關鍵字auto作存儲類別的聲明。
int f(int a) /*定義f函數,a為參數*/
{
auto int b,c=3; /*定義b,c自己主動變量*/
}
a是形參,b,c是自己主動變量,對c賦初值3。執行完f函數後,自己主動釋放a。b,c所占的存儲單元。
關鍵字auto能夠省略,auto不寫則隱含定為“自己主動存儲類別”,屬於動態存儲方式
。占用
棧空間
5.4 Register
為了提高效率,C語言同意將局部變量得值放在CPU中的寄存器中,這樣的變量叫“寄存器變量”,用關鍵字register作聲明。
int fac(int n)
{
register int i,f=1;
for(i=1;i<=n;i++)
f=f*I;
return(f);
}
int main(void)
{
int i;
for(i=0;i<=5;i++)
printf("%d!=%d\n",i,fac(i));
}
說明:
1)
僅僅有局部自己主動變量和形式參數能夠作為寄存器變量
。
2)一個計算機系統中的寄存器數目有限,不能定義隨意多個寄存器變量
。
3)局部靜態變量不能定義為寄存器變量
。
6. 總結
- 從變量的作用域(即從
空間
)角度來分
,能夠分為全局變量
和局部變量
。 - 從變量值存在的作時間(即
生存期
)角度來分
,能夠分為靜態存儲
方式和動態存儲
方式。 代碼段
、僅僅讀數據段
、讀寫數據段
、未初始化數據段
屬於靜態區域
靜態區域
: 是指在程序執行期間分配固定的存儲空間的方式堆
和棧
屬於動態區域
動態區域
: 是在程序執行期間依據須要進行動態的分配存儲空間的方式。代碼段
、僅僅讀數據段
和讀寫數據段
將在鏈接之後產生
未初始化數據段
將在程序初始化的時候開辟
- 而
堆
和棧
將在程序的執行中分配和釋放
。 - C語言程序分為
映像
和執行時
兩種狀態。在編譯-連接後形成的映像中
,將僅僅包括代碼段(Text)
、僅僅讀數據段(RO Data)
和讀寫數據段(RW Data)
。 - 在
程序執行之前
。將動態生成未初始化數據段(BSS
) - 在
程序的執行時
還將動態形成堆(Heap)
區域和棧(Stack)
區域。一般來說,在靜態的映像文件裏,各個部分稱之為
節(Section)
,而在執行時的各個部分稱之為段(Segment)
。假設不具體區分,能夠統稱為段
。 - C語言在
編譯和連接後
,將生成代碼段
(Text)、僅僅讀數據段(RO Data
)和讀寫數據段(RW Data
)。在
執行時
,除了以上三個區域外,還包括未初始化數據段(BSS
)區域和堆(Heap
)區域和棧(Stack
)區域。
7. 一些實例
const char ro[ ] = {"this is read only data"}; //僅僅讀數據區
static char rw_1[ ] ={"this is global read write data"}; //已初始化讀寫數據段
char BSS_1[ 100]; //未初始化數據段
const char *ptrconst ="constant data"; //字符串放在僅僅讀取數據段
int main()
{
short b; //在棧上。占用2個字節
char a[100]; //在棧上開辟100個字節, 它的值是其首地址
char s[ ]="abcdefg"; //s在棧上,占用4個字節,"abcdefg"本身放置在僅僅讀數據存儲區,占8個字節
char *p1; //p1在棧上。占用4個字節
char *p2="123456"; //p2 在棧上。p2指向的內容不能改,“123456”在僅僅讀數據區
static char rw_2[ ]={"this is local read write data"};//局部已初始化讀寫數據段
static char BSS_2[100]; //局部未初始化數據段
static int c = 0; //全局(靜態)初始化區
p1=(char *)malloc(10 * sizeof(char ) ); //分配內存區域在堆區
strcpy(p1,"xxxx"); //“XXXX”放在僅僅讀數據區。占5個字節
free(p1); //使用free釋放p1所指向的內存
return 0;
}
讀寫數據段包括了憶初始化的全局變量 static char rw_1[ ]以及局部靜態變量static rw_2[ ].其區別在於編繹時,是在函數內部使用的還是能夠在整個文件裏使用。對於rw_1[] 不管有無static 修飾。其都將被放置在讀寫數據區。僅僅是是否能被其他文件引用與否。
對於後者就不一樣了。它是局部靜態變量。放置在讀寫數據區,假設沒static修飾,其意義全然改變,它將會是開辟在棧空間的局部變量,而不是靜態變量。在這裏rw_1[],rw_2[]後沒具體數值。表示靜態區大小同後面字符串長度決定。
對於未初始化數據區BSS_1[100]與BSS_2[100]。其區別在於前者是全局變量。在所有文件裏都能夠使用;後者是局部變量,僅僅在函數內部使用。未初始化數據段不設置後面的初始化數值,因此必須使用數值指定區域的大小,編繹器將依據大小設置BSS中須要添加的長度。
參考文章
深入探討C語言中局部變量與全局變量在內存中的存放位置
C語言編程程序的內存怎樣布局
C語言編程程序的內存怎樣布局