在c語言中,每個變量和函數有兩個屬性:
數據類型
和數據的存儲類別
。
C語言中局部變量和全局變量變量的存儲類別(static
,extern
,auto
,register
)
空間
)角度來分生存期
)角度來分存放以下數據:
代碼段(text)
、只讀數據段(rodata)
、讀寫數據段(rwdata)
、未初始化數據段(bbs)
靜態存儲區存放全部的全局變量, 這些變量將在鏈接之後產生, 程序執行完畢就釋放, 程序執行的過程中它們占據固定的存儲單元, 而不會動態的進行分配和釋放
存放以下數據:
函數形參
、自動變量(未加static聲明的局部變量)
、函數調用時的現場保護和返回地址
對以上這些數據,在函數開始調用時分配動態存儲空間,函數結束時釋放這些空間。
一行一行的等待執行的機器碼
運行時
又可分為以下存儲區代碼段由程序中執行的機器代碼組成。在C語言中,程序語句進行編譯後,形成機器代碼。在執行程序的過程中,CPU的程序計數器指向代碼段的每一條
機器代碼
,並由處理器依次運行。
只讀數據段是程序使用的一些不會被更改的數據,使用這些數據的方式類似查表式的操作,由於這些變量不需要更改,因此只需要放置在只讀存儲器中即可。 只讀數據段由程序中所使用的數據產生,該部分數據的特點是在運行中不需要改變,因此編譯器會將該數據段放入只讀的部分中。C語言中的只讀全局變量
,只讀局部變量
,程序中使用的常量
等會在編譯時
被放入
到只讀數據區
。注意
:定義全局變量const char a[100]={“ABCDEFG”};將生成大小為100個字節的只讀數據區,並使用“ABCDEFG”初始化。如果定義為:const char a[ ]={“ABCDEFG”};則根據字符串長度生成8個字節的只讀數據段(還有’\0’),所以在只讀數據段中,一般都需要做完全的初始化。
#define A=18; ##常量
const int A = 18; ##只讀全局變量
int main(){
const int B = 18; ##只讀局部變量
}
已初始化數據是在程序中聲明,並且具有初值的變量,這些變量需要占用存儲器的空間,在程序執行時它們需要位於可讀寫的內存區域內,並具有初值,以供程序運行時讀寫。 全局變量全部存放在靜態存儲區,在程序開始執行時給全局變量分配存儲區
,程序行完畢就釋放
。在程序執行過程中它們占據固定的存儲單元
,而不動態地進行分配和釋放
;
全局變量
靜態(static) 局部變量
int global_init_val=1; ## 全局變量
int main(int argc, char * argv[]){
static int a=1; ## 靜態(static) 局部變量
}
未初始化數據是在程序中聲明,但是沒有初始化的變量,這些變量在程序運行之前不需要占用存儲器的空間。
int global_noinit_val; ## 全局未初始化全局變量
char *p1; ## 全局未初始化全局變量
int main(int argc, char * argv[]){
......
}
堆內存
只在程序運行時出現
,一般由程序員分配
和釋放
。
在具有操作系統的情況下,如果程序沒有釋放
,操作系統
可能在程序
(例如一個進程
)結束後回收內存
。
p1 = (char*) malloc(10); ## 分配得來的10和20個字節的區域就在堆區
p2 = (char*) malloc(20);
棧內存只在程序運行時出現
,在函數內部使用的變量
、函數的參數
以及返回值
將使用棧
空間, 棧空間由編譯器自動分配和釋放。棧空間
是動態開辟
與回收
的。在函數調用過程中,如果函數調用的層次比較多
,所需要的棧空間也逐漸加大
對於參數的傳遞
和返回值
,如果使用較大的結構體
,在使用的棧空間也會比較大
。
void main(void){
int b; ## 棧
char s[] = "abc"; ## 棧
char *p2; ## 棧
char *p3 = "123456"; ## 123456\0在常量區 ## p3 在棧上。
}
===================================華麗的分割線=========================
static
, extern
, auto
, register
)有時希望函數中的局部變量的值在函數調用結束後不消失而保留原值,這時就應該指定局部變量為“靜態局部變量”,用關鍵字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(對數值型變量)或空字符(對字符變量)。而對自動變量來說,如果不賦初值則它的值是一個不確定的值。
外部變量
(即全局變量
)是在函數的外部定義的,它的作用域為從變量定義處開始,到本程序文件的末尾。如果外部變量不在文件的開頭定義,其有效的作用范圍只限於定義處到文件終了。如果在定義點之前的函數想引用該外部變量,則應該在引用之前用關鍵字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。
函數中的局部變量,如不專門聲明為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不寫則隱含定為“自動存儲類別”,屬於動態存儲方式
。 占用棧空間
為了提高效率,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)局部靜態變量不能定義為寄存器變量
。
空間
)角度來分
,可以分為全局變量
和局部變量
。 從變量值存在的作時間(即生存期
)角度來分
,可以分為靜態存儲
方式和動態存儲
方式。
代碼段
、只讀數據段
、讀寫數據段
、未初始化數據段
屬於靜態區域
靜態區域
: 是指在程序運行期間分配固定的存儲空間的方式
堆
和棧
屬於動態區域
動態區域
: 是在程序運行期間根據需要進行動態的分配存儲空間的方式。
代碼段
、只讀數據段
和 讀寫數據段
將在鏈接之後產生
未初始化數據段
將在程序初始化的時候開辟
而堆
和棧
將在程序的運行中分配和釋放
。 C語言程序分為映像
和運行時
兩種狀態。在編譯-連接後形成的映像中
,將只包含代碼段(Text)
、只讀數據段(RO Data)
和讀寫數據段(RW Data)
。 在程序運行之前
,將動態生成未初始化數據段(BSS
) 在程序的運行時
還將動態形成堆(Heap)
區域和棧(Stack)
區域。一般來說,在靜態的映像文件中,各個部分稱之為節(Section)
,而在運行時的各個部分稱之為段(Segment)
。如果不詳細區分,可以統稱為段
。 C語言在編譯和連接後
,將生成代碼段
(Text)、只讀數據段(RO Data
)和讀寫數據段(RW Data
)。在運行時
,除了以上三個區域外,還包括未初始化數據段(BSS
)區域和堆(Heap
)區域和棧(Stack
)區域。
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中需要增加的長度。