1. 宣告和定義
函式和變數的宣告不會分配記憶體, 但是定義會分配相應的記憶體空間
函式和變數的宣告可以有很多次, 但是定義最多隻能有一次
函式的宣告和定義方式預設都是 extern 的, 即函式預設是全域性的
變數的宣告和定義方式預設都是區域性的, 在當前編譯單元或者檔案內可用
瞭解宣告和定義對static和extern的理解有輔助作用。比如extern就是在一處定義,其他檔案都只需要宣告即可,不可重複定義。
2. static& extern
2.1 static
一般區域性變數是儲存在棧區的,區域性變數的生命週期在其所在的語句塊執行結束時便結束了。但如果用static修飾區域性變數,那麼這個變數就不會儲存在棧區而是放在靜態資料區,其生命週期會一直持續到整個程式結束,該變數只在初次執行時進行初始化,且只進行一次,但是它的作用域只能是在函式裡面如下:
void print(){ static int z = 100; z++; cout << z <<endl; } int main(){ print(); print(); print(); return 0; }
區域性靜態變數z只能在本檔案的print函式裡面訪問,一旦超出作用域範圍,就無法訪問。
如果是static修飾的全域性變數,且實現的函式寫在標頭檔案(h)中,在其他檔案也可以訪問,如下:
// a.h #ifndef A_H #define A_H #include<iostream> using namespace std; static char str[] = "hello"; namespace sextern { void print1(); void Fun1(); void Fun1(){ str[0] = 'l'; } void print1(){ cout << "value " << str <<endl; cout << "address " << &str <<endl; } } #endif // A_H //b.h #ifndef B_H #define B_H namespace sextern{ void Fun2(){ str[0] = 'o'; } void print2(){ cout << "value " << str <<endl; cout << "address " << &str <<endl; } #endif // B_H //main.cpp #include "a.h" #include "b.h" using namespace sextern; int main(int argc, char *argv[]) { sextern::Fun1(); print1(); sextern::Fun2(); print2(); print1(); return 0; } //結果如下 /* * value lello * address 0x601058 * value oello * address 0x601058 * value oello * address 0x601058 * 按 <RETURN> 來關閉視窗... */
發現將static全域性變數寫在標頭檔案中,所有檔案的標頭檔案的操作都會共享這個變數。
但如果是在原始檔(cpp)中去操作這個靜態全域性變數,則這個靜態全域性變數只能在當前檔案有效,但是在另外一個檔案訪問此靜態變數,會是該變數初始的預設值,不會是其他檔案中修改的值,雖然它們有相同的初始內容,但是儲存的實體地址並不一樣。如下:
//a.h #ifndef A_H #define A_H #include<iostream> using namespace std; static char str[] = "hello"; namespace sextern { void Fun1(); void print1(); } #endif // A_H //a.cpp #include "a.h" namespace sextern { void Fun1(){ str[0] = 'l'; } void print1(){ cout << "value " << str << endl; cout << "address " << &str <<endl; } } //c.h #ifndef C_H #define C_H #include<iostream> using namespace std; namespace sextern { void Fun3(); void print3(); } #endif // C_H //c.cpp #include "c.h" #include "a.h" namespace sextern { void Fun3(){ str[0] = 'o'; } void print3(){ cout << "value " << str <<endl; cout << "address " << &str <<endl; } } #include "a.h" #include "c.h" using namespace sextern; int main(int argc, char *argv[]) { sextern::Fun1(); print1(); sextern::Fun3(); print3(); print1(); return 0; } //結果如下 /* * value lello * address 0x602064 * value oello * address 0x60205e * value lello * address 0x602064 * 按 <RETURN> 來關閉視窗... */
在a.h的標頭檔案中定義了一個靜態的全域性變數x,不同檔案的函式fun1和fun3會為每個包含該標頭檔案的cpp都建立一個全域性變數,但他們都是獨立的,只在該cpp檔案共享該變數。所以一般定義static全域性變數時,都把它放在原檔案中而不是標頭檔案,從而避免多個原始檔共享,就不會給其他模組造成不必要的資訊汙染。如果想要在不同檔案共享同一個全域性變數,這個時候就要用到extern。
2.2 extern
當在某個檔案定義了一個全域性變數,如果要在另一個檔案去使用該變數,如果再次去定義,則會出現重複定義的問題,這個時候就需要使用到宣告,對該變數的宣告告訴編譯器該變數在其他檔案中已經定義,在此處要去引用它。上述的程式碼改成如下形式:
//b.h #ifndef B_H #define B_H char str[] = "hello"; //定義一個全域性變數 #endif // B_H //a.h #ifndef A_H #define A_H #include<iostream> using namespace std; extern char str[]; namespace sextern { void Fun1(); void print1(); } #endif // A_H //a.cpp #include "a.h" namespace sextern { void Fun1(){ str[0] = 'l'; } void print1(){ cout << "value " << str << endl; cout << "address " << &str <<endl; } } //c.h #ifndef C_H #define C_H #include<iostream> using namespace std; extern char str[]; namespace sextern { void Fun3(); void print3(); } #endif // C_H //c.cpp #include "c.h" namespace sextern { void Fun3(){ str[0] = 'o'; } void print3(){ cout << "value " << str <<endl; cout << "address " << &str <<endl; } } //結果如下 /* * value lello * address 0x602058 * value oello * address 0x602058 * value oello * address 0x602058 * 按 <RETURN> 來關閉視窗... */
參考資料