引用自Cobing Liu
C語(yǔ)言代碼是以文件為單位來(lái)組織的舔痕,在一個(gè)源程序的所有源文件中,一個(gè)外部變量(注意不是局部變量)或者函數(shù)只能在一個(gè)源程序中定義一次宋渔,如果有重復(fù)定義的話編譯器就會(huì)報(bào)錯(cuò)彤委。伴隨著不同源文件變量和函數(shù)之間的相互引用以及相互獨(dú)立的關(guān)系驹暑,產(chǎn)生了extern和static關(guān)鍵字裳食。
下面矛市,詳細(xì)分析一下static關(guān)鍵字在編寫(xiě)程序時(shí)有的三大類(lèi)用法:
static全局變量
我們知道,一個(gè)進(jìn)程在內(nèi)存中的布局如圖1所示:
其中.text段保存進(jìn)程所執(zhí)行的程序二進(jìn)制文件诲祸,.data段保存進(jìn)程所有的已初始化的全局變量浊吏,.bss段保存進(jìn)程未初始化的全局變量(其他段中還有很多亂七八糟的段,暫且不表)烦绳。在進(jìn)程的整個(gè)生命周期中,.data段和.bss段內(nèi)的數(shù)據(jù)時(shí)跟整個(gè)進(jìn)程同生共死的配紫,也就是在進(jìn)程結(jié)束之后這些數(shù)據(jù)才會(huì)壽終就寢径密。
當(dāng)一個(gè)進(jìn)程的全局變量被聲明為static之后,它的中文名叫靜態(tài)全局變量躺孝。靜態(tài)全局變量和其他的全局變量的存儲(chǔ)地點(diǎn)并沒(méi)有區(qū)別享扔,都是在.data段(已初始化)或者.bss段(未初始化)內(nèi),但是它只在定義它的源文件內(nèi)有效植袍,其他源文件無(wú)法訪問(wèn)它惧眠。所以,普通全局變量穿上static外衣后于个,它就變成了新娘氛魁,已心有所屬,只能被定義它的源文件(新郎)中的變量或函數(shù)訪問(wèn)厅篓。
以下是一些示例程序:
file1.h如下:
#include
void printStr();
我們?cè)趂ile1.c中定義一個(gè)靜態(tài)全局變量hello, 供file1.c中的函數(shù)printStr訪問(wèn).
#include "file1.h"
static char* hello ="hello cobing!";
void printStr()
{
printf("%s\n", hello);
}
file2.c是我們的主程序所在文件秀存,file2.c中如果引用hello會(huì)編譯出錯(cuò).
#include "file1.h"
int main()
{
printStr();
printf("%s\n", hello);
return0;
}
報(bào)錯(cuò)如下:
[liujx@server235 static]$ gcc -Wall file2.c file1.c -o file2
file2.c: In function ‘main’:
file2.c:6: 錯(cuò)誤:‘hello’ 未聲明 (在此函數(shù)內(nèi)第一次使用)
file2.c:6: 錯(cuò)誤:(即使在一個(gè)函數(shù)內(nèi)多次出現(xiàn),每個(gè)未聲明的標(biāo)識(shí)符在其
file2.c:6: 錯(cuò)誤:所在的函數(shù)內(nèi)只報(bào)告一次羽氮。)
如果我們將file2.c改為下面的形式:
#include "file1.h"
int main()
{
printStr();
return 0;
}
則會(huì)順利編譯連接或链。
運(yùn)行程序后的結(jié)果如下:
[liujx@server235 static]$ gcc -Wall file2.c file1.c -o file2
[liujx@server235 static]$ ./file2
hello cobing!
上面的例子中,file1.c中的hello就是一個(gè)靜態(tài)全局變量档押,它可以被同一文件中的printStr調(diào)用澳盐,但是不能被不同源文件中的file2.c調(diào)用。
static局部變量
普通的局部變量在椓钏蓿空間上分配叼耙,這個(gè)局部變量所在的函數(shù)被多次調(diào)用時(shí),每次調(diào)用這個(gè)局部變量在棧上的位置都不一定相同粒没。局部變量也可以在堆上動(dòng)態(tài)分配旬蟋,但是記得使用完這個(gè)堆空間后要釋放之。
static局部變量中文名叫靜態(tài)局部變量革娄。它與普通的局部變量比起來(lái)有如下幾個(gè)區(qū)別:
- 位置:靜態(tài)局部變量被編譯器放在全局存儲(chǔ)區(qū).data(注意:不在.bss段內(nèi)倾贰,原因見(jiàn)3))冕碟,所以它雖然是局部的,但是在程序的整個(gè)生命周期中存在匆浙。
- 訪問(wèn)權(quán)限:靜態(tài)局部變量只能被其作用域內(nèi)的變量或函數(shù)訪問(wèn)安寺。也就是說(shuō)雖然它會(huì)在程序的整個(gè)生命周期中存在,由于它是static的首尼,它不能被其他的函數(shù)和源文件訪問(wèn)挑庶。
- 值:靜態(tài)局部變量如果沒(méi)有被用戶初始化,則會(huì)被編譯器自動(dòng)賦值為0软能,以后每次調(diào)用靜態(tài)局部變量的時(shí)候都用上次調(diào)用后的值迎捺。這個(gè)比較好理解,每次函數(shù)調(diào)用靜態(tài)局部變量的時(shí)候都修改它然后離開(kāi)查排,下次讀的時(shí)候從全局存儲(chǔ)區(qū)讀出的靜態(tài)局部變量就是上次修改后的值凳枝。
以下是一些示例程序:
file1.h的內(nèi)容和上例中的相同,file1.c的內(nèi)容如下:
#include "file1.h"
void printStr()
{
int normal = 0;
static int stat = 0;//this is a static local var
printf("normal = %d ---- stat = %d\n",normal, stat);
normal++;
stat++;
}
為了便于比較跋核,我定義了兩個(gè)變量:普通局部變量normal和靜態(tài)局部變量stat岖瑰,它們都被賦予初值0;
file2.c中調(diào)用file1.h:
#include "file1.h"
int main()
{
printStr();
printStr();
printStr();
printStr();
printf("call stat in main: %d\n",stat);
return0;
}
這個(gè)調(diào)用會(huì)報(bào)錯(cuò)砂代,因?yàn)閒ile2.c中引用了file1.c中的靜態(tài)局部變量stat蹋订,如下:
[liujx@server235 static]$ gcc -Wall file2.c file1.c -o file2
file2.c: In function ‘main’:
file2.c:9: 錯(cuò)誤:‘stat’ 未聲明 (在此函數(shù)內(nèi)第一次使用)
file2.c:9: 錯(cuò)誤:(即使在一個(gè)函數(shù)內(nèi)多次出現(xiàn),每個(gè)未聲明的標(biāo)識(shí)符在其
file2.c:9: 錯(cuò)誤:所在的函數(shù)內(nèi)只報(bào)告一次刻伊。)
編譯器說(shuō)stat未聲明露戒,這是因?yàn)樗床坏絝ile1.c中的stat,下面注掉這一行:
#include "file1.h"
intmain()
{
printStr();
printStr();
printStr();
printStr();
// printf("call stat in main: %d\n",stat);
return0;
}
[liujx@server235 static]$ gcc -Wall file2.c file1.c -o file2
[liujx@server235 static]$ ./file2
normal = 0 ---- stat = 0
normal = 0 ---- stat = 1
normal = 0 ---- stat = 2
normal = 0 ---- stat = 3
運(yùn)行如上所示捶箱∶捣妫可以看出,函數(shù)每次被調(diào)用讼呢,普通局部變量都是重新分配撩鹿,而靜態(tài)局部變量保持上次調(diào)用的值不變。
需要注意的是由于static局部變量的這種特性悦屏,使得含靜態(tài)局部變量的函數(shù)變得不可重入节沦,即每次調(diào)用可能會(huì)產(chǎn)生不同的結(jié)果。這在多線程編程時(shí)可能會(huì)成為一種隱患础爬。需要多加注意甫贯。
static函數(shù)
相信大家還記得C++面向?qū)ο缶幊讨械膒rivate函數(shù),私有函數(shù)只有該類(lèi)的成員變量或成員函數(shù)可以訪問(wèn)看蚜。在C語(yǔ)言中叫搁,也有“private函數(shù)”,它就是接下來(lái)要說(shuō)的static函數(shù),完成面向?qū)ο缶幊讨衟rivate函數(shù)的功能渴逻。
當(dāng)你的程序中有很多個(gè)源文件的時(shí)候疾党,你肯定會(huì)讓某個(gè)源文件只提供一些外界需要的接口,其他的函數(shù)可能是為了實(shí)現(xiàn)這些接口而編寫(xiě)惨奕,這些其他的函數(shù)你可能并不希望被外界(非本源文件)所看到雪位,這時(shí)候就可以用static修飾這些“其他的函數(shù)”。
所以static函數(shù)的作用域是本源文件梨撞,把它想象為面向?qū)ο笾械膒rivate函數(shù)就可以了雹洗。
下面是一些示例:
file1.h如下:
#include
static int called();
void printStr();
file1.c如下:
#include "file1.h"
static int called()
{
return6;
}
void printStr()
{
int returnVal;
returnVal = called();
printf("returnVal=%d\n",returnVal);
}
file2.c中調(diào)用file1.h中聲明的兩個(gè)函數(shù),此處我們故意調(diào)用called():
#include "file1.h"
int main()
{
int val;
val = called();
printStr();
return0;
}
編譯時(shí)會(huì)報(bào)錯(cuò):
[liujx@server235 static]$ gcc -Wall file2.c file1.c -o file2
file1.h:3: 警告:‘called’ 使用過(guò)但從未定義
/tmp/ccyLuBZU.o: In function `main':
file2.c:(.text+0x12): undefined reference to `called'
collect2: ld 返回 1
因?yàn)橐昧薴ile1.h中的static函數(shù)卧波,所以file2.c中提示找不到這個(gè)函數(shù):undefined reference to 'called'
下面修改file2.c:
#include "file1.h"
int main()
{
printStr();
return0;
}
編譯運(yùn)行:
[liujx@server235 static]$ gcc -Wall file2.c file1.c -o file2
[liujx@server235 static]$ ./file2
returnVal=6
static函數(shù)可以很好地解決不同原文件中函數(shù)同名的問(wèn)題时肿,因?yàn)橐粋€(gè)源文件對(duì)于其他源文件中的static函數(shù)是不可見(jiàn)的。