1. 程式人生 > >C/C++關於全域性變數和區域性變數初始化與不初始化的區別

C/C++關於全域性變數和區域性變數初始化與不初始化的區別

在C語言裡,全域性變數如果不初始化的話,預設為0,也就是說在全域性空間裡:int x =0; 跟 int x; 的效果看起來是一樣的。但其實這裡面的差別很大,強烈建議大家所有的全域性變數都要初始化,他們的主要差別如下:

編譯器在編譯的時候針對這兩種情況會產生兩種符號放在目標檔案的符號表中,對於初始化的,叫強符號,未初始化的,叫弱符號。聯結器在連線目標檔案的時候,如果遇到兩個重名符號,會有以下處理規則:

1、如果有多個重名的強符號,則報錯。

2、如果有一個強符號,多個弱符號,則以強符號為準。

3、如果沒有強符號,但有多個重名的弱符號,則任選一個弱符號。

    大部分情況下,我們不希望聯結器為我們做決定,所以我不是很認同後兩個規則,至少應該給個警告,

而不應該安靜地通過。因為這種問題引起的bug會很難查,所以我們要儘量把全域性變數初始化,對於不想給別的檔案引用的變數,也儘量用static修飾。除了連線時的表現不一樣外,未初始化的符號在目標檔案的bss段中,而初始化的符號在data段中。

注: bss段(未手動初始化的資料)並不給該段的資料分配空間,只是記錄資料所需空間的大小。
        data段(已手動
初始化的資料)則為資料分配空間,資料儲存在目標檔案中。

對於區域性變數,不被初始化的話,其值一般分為兩種情況Debug版和Release版的區別。

例:

#include "stdafx.h"
int i;
int main(int argc, char* argv[])
{
printf(" i = %d\n",i);
int j;
printf(" j= %d\n",j);
return 0;
}

在Debug版下,在這段程式碼中i的值打印出來是0,而j的值打印出來是-858993460,也就是0xCCCCCCCC.至於為什麼是這個值,有網友給出這個解釋。(設計成0xcccccccc是有特殊用意的……這個好像叫做Poison,未初始化的Pointer去取值的話會出錯。肯定有人問為什麼不弄成0x00000000,因為空指標是指針的有效狀態,可能會誤導人,而0xCCCCCCCC在Windows下永遠不可能是一個指標的有效狀態(不是NULL,不指向一個物件,不指向一堆物件緊接之後的區域),這就是在模擬野指標……)。

值得注意的是,同樣的程式碼在Release版下,這段程式碼中未被初始化的變數最後打印出來的可能都是0。

也有強大的網友給出解釋。(重點在於vc的一個功能:Catch release-build errors in debug build用/GZ編譯開關開啟。debug版這個開關是開的,release版是關的(為了效率)。這個開關說白了就是把所有動態區域性變數初始化成0xcccccccc,把所有動態堆變數初始化成0xcdcdcdcd。很多新手會忘記初始化這些本來應該初始化的變數(尤其是new出來的變數),有時他們會假定這些變數應該是0,這樣就可能出現在release版正常而debug版不正常的程式,因為release版至少區域性變數的初始值很可能就是0,而有時他們又會假定或者期望這些變數不是0,這樣就帶了一個最難發現的bug)。

鑑於上面分析,建議大家養成初始化一個變數的習慣。