在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 ;
}
程式的運作結果是:
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 ;
}
上面的程式會輸出什麼呢?
答案是:報錯,找不到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指針。