一、初始化規則部分
在說明為什么要初始化之前,先提及下 C 語言的初始化規則,以備后用。
可能大家在對數組進行初始化時用的是這樣的方法:
char buf[10] = {0};
那么 char buf[10] = {1};是不是將每個數組中的每個元素都初始化為 1 了呢?
其實不然,根據編譯器的特性,在指定初始化元素時,如果元素的個數少於數組元素的總個數,那么其它的元素將會初始化為 0。
我們可以用一段代碼來驗證這個特性:
1 #include <stdio.h> 2 3 int main() 4 { 5 char buf[10] = {1}; 6 7 return 0; 8 }
其反匯編代碼如下:
1 <main>: 2 push {fp} ; (str fp, [sp, #-4]!) 3 add fp, sp, #0 4 sub sp, sp, #20 5 sub r3, fp, #16 6 mov r2, #0 7 str r2, [r3] 8 str r2, [r3, #4] 9 strh r2, [r3, #8] 10 mov r3, #1 11 strb r3, [fp, #-16] 12 mov r3, #0 13 mov r0, r3 14 add sp, fp, #0 15 pop {fp} ; (ldr fp, [sp], #4) 16 bx lr
由其中的部分編譯代碼 6-11 行可知,程序只是將數組的第一個字節賦值為 1,而其余字節被賦值為 0。到此可以得出結論,並非是所有的元素都被初始化為 1。
二、全局變量初始化部分
下面說一下我們創建的全局變量時為什么要初始化,如果想創建一個初始值為 0 的變量,那么 “ = 0 ” 的賦值操作可不可以省略呢?
費話不多說,用代碼來說明一切。
假如有以下場景,某公司為實現一個項目,A程序員編寫了 module_a.c 程序,他的同事B編寫了 module_b.c 程序,最終兩個人編寫的源碼如下:
1 /* module_a.c */ 2 #include <stdio.h> 3 4 void function(void); 5 6 int global = 0; 7 8 int main() 9 { 10 global = 3; 11 function(); 12 printf("main: %d \n", global); 13 return 0; 14 }
1 /* module_b.c */ 2 #include <stdio.h> 3 4 int global; 5 6 void function(void) 7 { 8 global = 6; 9 printf("function: %d \n", global); 10 return 0; 11 }
二人心有靈犀,定義了同樣的全局變量 global,但由於 B 一時的疏忽,忘了將 global 進行初始化。
編譯竟可以正常通過,且運行結果如下:
function: 6 main: 6
我們可以看到,同事 A 編譯的模塊產生了莫名其妙的運行結果。為什么定義了同名的全局變量,編譯器不會報錯呢? 我們繼續探索。
首先將源文件分別編譯為目標文件,然后使用 readelf 工具查看內容。
arm-linux-gcc -c module_x.c
arm-linux-readelf -a module_x.o
作者只摘取了其中的有關內容,其中 a 模塊中的 global 標識如下:
... [4] .bss NOBITS 00000000 000078 000004 00 WA 0 0 4 ... 13: 00000000 4 OBJECT GLOBAL DEFAULT 4 global ...
b 模塊中的 global 標識如下:
... 12: 00000004 4 OBJECT GLOBAL DEFAULT COM global ...
由此可得知,初始化為 0 的全局變量是合並至 .bss 段,而未初始化的全局變量合並到了 COM(common block) 段。原因是 gcc 編譯器的缺省行為和傳統 unix c 編譯器一致,將未初始化的全局變量放入到 common block 中, common block 相當於弱符號(weak symbol),所以鏈接的時候並不會報錯,這或許可能是一個很難被找出的 BUG。
在網絡上找到了以下總結:
同名的弱符號和 global 符號鏈接不會出錯,鏈接器會選擇 global 符號。同理,如果有一個 global 符號和多個在 common block 中的重名,那么鏈接器會選取global符號。換句話說,鏈接器認為未初始化的全局變量是weak symbol。
當然,避免這種情況發生的辦法也是有的,我們可以在編譯時加上 -fno-common 屬性來關閉 gcc 的這個特性,如果有同名的全局變量,鏈接時就會產生出錯誤,便於用戶知曉問題的所在。