在C語言中:
隱藏
很多人經常會忘了這一條。其實這個作用很常用也很重要。
當我們同時編譯多個文件時,所有未加static前綴的全局變量和函數都具有全局可見性。
爲理解這句話,我舉例來說明。我們要同時編譯兩個源文件,一個是a.c,另一個是main.c。
char a = 'A'; // global variable
void msg()
{
printf("Hello\n");
}
int main(void)
{
extern char a;
printf("%c ", a);
(void)msg();
return 0;
}
程序的運行結果是:
A Hello
爲什麼在a.c中定義的全局變量a和函數msg能在main.c中使用?
前面說過,所有未加static前綴的全局變量和函數都具有全局可見性,其它的源文件也能訪問。
此例中,a是全局變量,msg是函數,並且都沒有加static前綴,因此對於另外的源文件main.c是可見的。
如果加了static,就會對其它源文件隱藏。
static char a = 'A'; // global variable
static void msg()
{
printf("Hello\n");
}
int main(void)
{
printf("%c ", a);
(void)msg();
return 0;
}
上面的程序會輸出什麼呢?
答案是:報錯,找不到a與msg的定義。
在a和msg的定義前加上static,main.c就看不到它們了。
利用這一特性可以在不同的文件中定義同名函數和同名變量,而不必擔心命名衝突。
static可以用作函數和變量的前綴,對於函數來講,static的作用僅限於隱藏。
在上面的例子中,含有一個新的關鍵字——extern
下面來講一下static和extern的區別:
extern
extern告訴編譯器這個變量或函數在其他文檔裏已被定義了。
看下面的例子:
static int i; //只在a文檔中用
int j; //在工程裏用
static void init() //只在a文檔中用
{
}
void callme() //在工程中用
{
static int sum;
}
extern int j; //調用a文檔裏的
extern void callme(); //調用a文檔裏的
int main()
{
...
}
上面的全局i變量和init()函數只能用在a.c文檔中,全局變量sum的作用域只在callme裏。變量j和函數callme()的全侷限擴充到整個工程文檔。所以能夠在下面的b.c中用extern關鍵字調用。extern告訴編譯器這個變量或函數在其他文檔裏已被定義了。
extern C
extern的另外用法是當C和C++混合編程時假如c++調用的是c源文檔定義的函數或變量,那麼要加extern來告訴編譯器用c方式命名函數:
extern "C" //在c++文檔裏調用c文檔中的變量
{
int j;
void callme();
}
int main()
{
callme();
}
static法則:
A、若全局變量僅在單個C文檔中訪問,則能夠將這個變量修改爲靜態全局變量,以降低模塊間的耦合度;
B、若全局變量僅由單個函數訪問,則能夠將這個變量改爲該函數的靜態局部變量,以降低模塊間的耦合度;
C、設計和使用訪問動態全局變量、靜態全局變量、靜態局部變量的函數時,需要考慮重入問題;
變量
1.變量定義的一般形式
存儲類別數據類型變量表;
2.變量定義的作用
①規定了變量的取值範圍。
②規定了變量進行的運行操作。
③規定了變量的作用域。
④規定了變量的存儲方式。
⑤規定了變量佔用的存儲空間。
3.局部變量和全局變量
從作用域角度將變量分爲局部變量和全局變量。它們採取的存儲類別如下:
局部變量:
①自動變量,即動態局部變量(離開函數,值就消失)。
②靜態局部變量(離開函數,值仍保留)。
③寄存器變量(離開函數,值就消失)。
④形式參數可以定義爲自動變量或寄存器變量。
全局變量:
①靜態外部變量(只限本程序文件使用)。
②外部變量(即非靜態的外部變量,允許其它程序文件引用)。
動態存儲和靜態存儲
從變量存在時間可將變量存儲分爲動態存儲和靜態存儲。
靜態存儲是在整個程序運行時都存在,而動態存儲則是在調用函數時臨時分配存儲單元。
動態存儲:
①自動變量(函數內有效)。
②寄存器變量(函數內有效)。
③形式參數。
靜態存儲:
①靜態局部變量(函數內有效)。
②靜態外部變量(本程序文件內有效)。
③外部變量(整個程序可引用)。
靜態存儲區和動態存儲區
從變量值存放的位置可將變量存儲區分爲靜態存儲區和動態存儲區:
內存中靜態存儲區:
①靜態局部變量。
②靜態外部變量。
③外部變量(可被同一程序其它文件引用)。
內存中動態存儲區:自動變量和形式參數。
CPU中的寄存器:寄存器變量。
全局變量
全局變量有外部、靜態兩種存儲方式。
外部全局變量
全局變量一般用外部存儲方式存儲,用保留字extern加以定義。此時,變量的作用域是構成整個程序的所有程序文件,也就是定義的外部變量可供其它程序文件使用。
使用這樣的全局變量一定要非常慎重,一旦產生錯誤,將波及整個程序。
靜態全局變量
如果希望全局變量僅限於本程序文件使用,而其它程序文件中不能引用,這時必須將其存儲方式定義爲靜態存儲方式,用保留字static加以定義。此時稱爲靜態外部變量。
例如,在文件filel.c中,如果作這樣的定義:
static int a:
則變量a的作用域被縮小至本程序文件filel1.c,文件file2.c中不能引用。
值得注意的是對全局變量加static,定義爲靜態存儲方式,並不意味着是靜態存儲;而不加static,是動態存儲。
兩種形式的全局變量(外部變量)都是靜態存儲方式,都是編譯時分配存儲空間,但作用域不同。使用靜態外部變量,有利於隔離錯誤,有利於模塊化程序設計。
全局變量的缺省存儲方式是外部存儲方式。
前面章節中的程序沒有見到變量的存儲類別定義,實際上採用變量的缺省存儲方式。對局部變量採用auto方式,對全局變量採用extern方式。這也是至今爲止,我們在程序中沒有見到auto、extern等的原因。
至此,我們對變量的存儲類別及數據類型進行了全面討論,在此作個小結。
局部靜態變量
在C/C++中, 局部變量按照存儲形式可分爲三種auto, static, register。其中register不常用到
下面主要說說auto和static的區別。
1. 存儲空間分配和生存週期不同
auto類型局部變量就是普通的局部變量(不加修飾的局部變量默認爲該類型)。該類型局部變量存儲在棧上,在動態存儲區,生命週期僅限於定義它的函數,函數結束,它就自動釋放。static類型局部變量存儲在靜態存儲區,在程序整個運行期間都不釋放。兩者之間的作用域相同,但生存期不同。
2. static局部變量在所處模塊在初次運行時進行初始化工作,且只操作一次。
3. 對於局部靜態變量,如果不賦初值,編譯期會自動賦初值0或空字符,而auto類型的初值是不確定的。
對於C++中的class對象例外,class的對象實例如果不初始化,則會自動調用默認構造函數,不管是否是static類型
特點: static局部變量的”記憶性”與生存期的”全局性”
外部靜態變量/函數
在C語言中 static有了第二種含義:
用來表示不能被其它文件訪問的全局變量和函數。
但爲了限制全局變量/函數的作用域, 函數或變量前加static使得函數成爲靜態函數。
但此處“static”的含義不是指存儲方式,而是指對函數的作用域僅侷限於本文件(所以又稱內部函 數)。
注意此時, 對於外部(全局)變量, 不論是否有static限制, 它的存儲區域都是在靜態存儲區,生存期都是全局的. 此時的static只是起作用域限制作用, 限定作用域在本模塊(文件)內部.
使用內部函數的好處是:不同的人編寫不同的函數時,不用擔心自己定義的函數,是否會與其它文件中的函數同名。
靜態數據成員/成員函數
前兩種C和C++都有,這種僅在C++中有,下面作以下介紹:
C+ +重用了這個關鍵字,並賦予它與前面不同的第三種含義:
表示屬於一個類而不是屬於此類的任何特定對象的變量和函數.
這是與普通成員函數的最大區別,也是其應用所在。
比如在對某一個類的對象進行計數時, 計數生成多少個類的實例,就可以用到靜態數據成員。
在這裏面, static既不是限定作用域的, 也不是擴展生存期的作用, 而是指示變量/函數在此類中的唯一性. 這也是”屬於一個類而不是屬於此類的任何特定對象的變量和函數”的含義.。
因爲它是對整個類來說是唯一的,因此不可能屬於某一個實例對象的.
針對靜態數據成員而言, 成員函數不管是否是static, 在內存中只有一個副本, 普通成員函數調用時, 需要傳入this指針, static成員函數調用時, 沒有this指針。