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