看到有一位同學在標頭檔中這麼寫:
static const wchar_t* g_str1 = …static const wchar_t* g_str2 = …
這種定義變數的方式我從來沒有見過,而且它還能順利通過編譯,於是我很想知道編譯器是如何處理這種變數定義的。
定義全域變數時使用static,意味著該變數的範圍只限於定義它的源檔案中,其它源檔案不能訪問。既然這種定義方式出現在標頭檔中,那麼可以很自然地推測:包含了該標頭檔的所有源檔案中都定義了這些變數,即該標頭檔被包含了多少次,這些變數就定義了多少次。
假如將上面兩行代碼的static去掉,編譯的時候就會出現變數重定義的錯誤,這進一步證實了上面的推測,因為沒有static的話變數的範圍是全域的,定義了兩個以上的同名變數就會出現該錯誤。
推測終究是推測,要真正證實這個推測還要通過寫代碼來驗證。驗證的方式是:在標頭檔中使用static定義變數,在多個源檔案中包含該標頭檔,然後在每個源檔案中輸出變數的地址,同時在一個源檔案中改變變數的值並輸出,在另一個源檔案中也輸出。如果每個源檔案的輸出都不同,則推測得證;否則推測是錯誤的。
下面是定義變數的標頭檔的代碼:
//Header.h#pragma oncestatic int g_int = 3;
接下來在另一個標頭檔中聲明兩個測試函數:
//Functions.h#pragma oncevoid TestSource1();void TestSource2();
分別在兩個源檔案中定義這兩個測試函數:
//Source1.cpp#include <stdio.h>#include "Header.h"void TestSource1() {wprintf(L"g_int's address in Source1.cpp: %08x\n", &g_int);g_int = 5;wprintf(L"g_int's value in Source1.cpp: %d\n", g_int);}
//Source2.cpp#include <stdio.h>#include "Header.h"void TestSource2() {wprintf(L"g_int's address in Source2.cpp: %08x\n", &g_int);wprintf(L"g_int's value in Source2.cpp: %d\n", g_int);}
最後在main函數中調用這兩個測試函數:
//Main.cpp#include "Functions.h"int wmain() {TestSource1();TestSource2();}
運行該程式:
可以看到,雖然在代碼中好像使用了相同的變數,但是實際上使用的是不同的變數,在每個源檔案中都有單獨的變數。所以,在標頭檔中定義static變數會造成變數多次定義,造成記憶體空間的浪費,而且也不是真正的全域變數。應該避免使用這種定義方式。
作為對比,下面使用正確的方式來定義全域變數:
//Header.h#pragma onceextern int g_int;
//Source1.cpp#include <stdio.h>#include "Header.h"int g_int = 3;void TestSource1() {wprintf(L"g_int's address in Source1.cpp: %08x\n", &g_int);g_int = 5;wprintf(L"g_int's value in Source1.cpp: %d\n", g_int);}
其它檔案不變。
運行程式:
可以看到,這次兩個源檔案中使用的都是同一個變數。要注意的是,使用extern聲明變數時不能帶有初始值,否則仍然屬於變數定義,會出現變數重定義的錯誤。