在c語言中
1.先來介紹它的第一條也是最重要的一條:隱藏原則 這是被static所修飾的函數和變量共同遵循的原則
當我們同時編譯多個文件時,所有未加static前綴的全局變量和函數都具有全局可見性。注意是未加static的全局的函數和全局的變量具有全局性即它們可以被其他.c文件訪問。爲理解這句話,我舉例來說明。我們要同時編譯兩個源文件,一個是test.c,另一個是main.c
/* test.c */
char t = 'T'; // 全局變量
void hello()
{
printf("hello\n");
}
/* main.c 爲了說明問題就不再寫test.h 文件 啦*/
int main(void)
{
extern char t; // 外部變量必須先聲明後使用
hello(); // 外部函數不需要
printf("%c", t);
....
}
程序的運行結果是:Hello T
你可能會問:爲什麼在test.c中定義的全局變量t和函數hello能在main.c中使用?前面說過,所有未加static前綴的全局變量和函數都具有全局可見性,其它的源文件也能訪問。此例中,t是全局變量,hello是函數,並且都沒有加static前綴,因此對於另外的源文件main.c是可見的。
如果加了static,就會對其它源文件隱藏。例如在t和hello的定義前加上static,main.c就看不到它們了。利用這一特性可以在不同的文件中定義同名函數和同名變量,而不必擔心命名衝突。
static可以用作函數和變量的前綴,對於函數來講,static的作用僅限於隱藏,而對於變量,static還有下面兩個作用
2.static的第二個作用是保持變量內容的持久
存儲在靜態數據區的變量會在程序剛開始運行時就完成初始化,也是唯一的一次初始化。共有兩種變量存儲在靜態存儲區:全局變量和static變量,只不過和全局變量比起來,static可以控制變量的可見範圍,說到底static還是用來隱藏的。對比全局變量和全局靜態變量尤其明顯,二者都存儲在靜態區初始化和持久性相同,唯一不同的是否可以被其他文件訪問;靜態局部變量由於本身就具備隱藏性所以static修飾的局部變量初始化和持久性顯得會明顯
int fun(void)
{ static int c = 10; // 事實上此賦值語句從來沒有執行過(個人覺的這句話有點問題,應該是隻在第一次調用fun()函數的時候進行初始化。)
return c--;
}
int c = 1;
void main()
{
printf("global\t\t local static \n");
for(; c <= 10; ++c)
printf(%d\t\t%d\n", c, fun());
}
程序的運行結果是:global local static
1 10
2 9
3 8
4 7
5 6
6 5
7 4
8 3
9 2
10 1
3 static的第三個作用是默認初始化爲0 其實全局變量也具備這一屬性,因爲都存儲在靜態數據區
在靜態數據區,內存中所有的字節默認值都是0x00,某些時候這一特點可以減少程序員的工作量。比如初始化一個稀疏矩陣,我們可以一個一個地把所有元素都置0,然後把不是0的幾個元素賦值。如果定義成靜態的,就省去了一開始置0的操作;再比如要把一個字符數組當字符串來用,但又覺得每次在字符組末尾加‘\0’太麻煩。如果把字符串定義成靜態的,就省去了這個麻煩,因爲那裏本來就是‘\0’
在C++中
除了具備C語言的以上三個原則外,位於C++ 類內的static原則: 被static修飾的類內的變量和函數是屬於類的而不是該類實例的
在一個.cpp文件中可能會出現下面情況:
/* IPCThreadState.h 在頭文件中聲明類IPCThreadState*/
class IPCThreadState
{
public:
static IPCThreadState* self(); // 方法聲明而且有static修飾
int getCallingPid();
........
........
}
/* IPCThreadState.cpp 在該源文件中定義類IPCThreadState*/
// 以下這些全局函數和變量沒有聲明在IPCThreadState.h中即不是類的成員函數和變量 所以他們遵循前邊C語言中的原則
static const char* getReturnString(size_t idx); // 聲明
static const char* getReturnString(size_t idx) // 定義
{
..........
}
static bool gHaveTLS = false;
static bool gShutdown = false;
static bool gDisableBackgroundScheduling = false;
// 是類的成員函數 且是static 所以該函數是屬於類IPCThreadState的 服務類的所有實例
IPCThreadState* IPCThreadState::self() // 定義
{
。。。
}
// 是類的成員函數 沒有static是普通類成員 該函數屬於類的某一個實例
int IPCThreadState::getCallingPid()
{
return mCallingPid;
}
總之, 在類中的static 的數據成員和成員函數 是屬於類的 這就是面向對象程序裏邊static的原則