本文介紹C/C++的全局變量初始化與不初始化的區別。
在C語言里,全局變量如果不初始化的話,默認為0,也就是說在全局空間里:
int x =0; 跟 int x; 的效果看起來是一樣的。但其實這里面的差別很大,強烈建議大家所有的全局變量都要初始化,他們的主要差別如下:
編譯器在編譯的時候針對這兩種情況會產生兩種符號放在目標文件的符號表中,對於初始化的,叫強符號,未初始化的,叫弱符號。
連接器在連接目標文件的時候,如果遇到兩個重名符號,會有以下處理規則:
1、如果有多個重名的強符號,則報錯。
2、如果有一個強符號,多個弱符號,則以強符號為准。
3、如果沒有強符號,但有多個重名的弱符號,則任選一個弱符號。
基於以上規則看下面的程序:(編譯器為gcc 3.4.6, VC下結果不一樣)
main.cpp int x; void foo(); int main(int argc, char* argv[]) { printf("x1:%d\n", x); foo(); printf("x2:%d\n", x); return 0; } int x; void foo() { x = 2; } |
因為兩個文件里面的x都被初始化了,所以編譯出來的兩個目標文件里x都是強符號,連接的時候會報錯:
multiple definition of `x'
符合規則1。
把var.cpp里面的int x = 0;改成 int x; 不做初始化,編譯、連接無任何警告,運行結果為:
x:1
x:2
說明連接的時候以main.cpp中的x為准,foo函數修改的是main.cpp中定義的x。符合規則2。
把main.cpp中的初始化也去掉,改成 int x; 編譯、連接仍然很順利,運行結果為:
x:1
x:2
說明main函數和foo函數修改的是同一個x,連接器自己選擇了一個x,符合規則3.
大部分情況下,我們不希望連接器為我們做決定,所以我不是很認同后兩個規則,至少應該給個警告,而不應該安靜地通過。
也許寫var.cpp的人根本不知道main.cpp里面也有一個x呢,foo函數的本意也許並不是要修改main.cpp中的x。因為這種問題引起的bug會很難查。
所以我們要盡量把全局變量初始化,對於不想給別的文件引用的變量,也盡量用static修飾。
除了連接時的表現不一樣外,為初始化的符號在目標文件的bss段中,而初始化的符號在data段中。