印象中以前學過C語言中跨檔案的全域變數是這樣宣告的。
檔案一:int g_var1;
檔案二:extern int g_val1;
然而,我從來沒有想過如果沒有extern的情況會發生什麼狀況。加上之前看過的objdump和nm後手癢,所以把可能的排列組合看看可能發生什麼事。
這邊要先知道common object表示object檔案已經紀錄下這個symbol,最後link的時候才會決定要怎麼處理。會放在common section 出處。
由於結果和分析又臭又長,連我自己都不太想回去看。建議先看結論,有疑惑再回來看結果和分析吧。
目錄
測試環境
1 2 3 4 5 6 |
|
- GNU objdump (GNU Binutils for Ubuntu) 2.24
- GNU nm (GNU Binutils for Ubuntu) 2.24
測試程式
非常簡單,就是有兩個檔案存放全域變數,每次測試時先更改全域變數
* 是否有初始值
* 宣告方式是否有extern
全部的排列組合放在這邊
1
|
|
1
|
|
1 2 3 4 5 6 7 |
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
|
測試結果
var1.c 宣告方式 | var2.c宣告方式 | 編譯結果 | nm 和 objdump 分析 |
---|---|---|---|
int g_var; | int g_var; | 成功 | 分析 |
int g_var = 2; | int g_var; | 成功 | 分析 |
int g_var; | int g_var = 3; | 成功 | 分析 |
int g_var = 2; | int g_var = 3; | 失敗 | 分析 |
var1.c 宣告方式 | var2.c宣告方式 | 編譯結果 | nm 和 objdump 分析 |
---|---|---|---|
int g_var; | extern int g_var; |
成功 | 分析 |
int g_var = 2; | extern int g_var; |
成功 | 分析 |
int g_var; | extern int g_var = 3; |
有警告 | 分析 |
int g_var = 2; | extern int g_var = 3; |
失敗 | 分析 |
var1.c 宣告方式 | var2.c宣告方式 | 編譯結果 | nm 和 objdump 分析 |
---|---|---|---|
extern int g_var; |
extern int g_var; |
link失敗 | 分析 |
extern int g_var = 2; |
extern int g_var; |
有警告 | 分析 |
extern int g_var |
extern int g_var = 3; |
有警告 | 分析 |
extern int g_var = 2; |
extern int g_var = 3; |
link失敗 | 分析 |
分析 var1.c: int g_var; var2.c: int g_var; 的結果
objdump -t
顯示object檔案的symbol table欄位資料:- var1.o: 0000000000000004 O COM 0000000000000004 g_var1
- 這是一個object,放在commom section
- var2.o: 0000000000000004 O COM 0000000000000004 g_var1
- 這是一個object,放在commom section。
- main.o: 0000000000000000 UND 0000000000000000 g_var1
- 這是一個undefined symbol
- glvar: 0000000000601044 g O .bss 0000000000000004 g_var1
- 這個symbol放在
.bss
,也就是未初始化的全域變數 - 這是最後產生的executable object
- 這個symbol放在
- var1.o: 0000000000000004 O COM 0000000000000004 g_var1
nm -A
顯示object檔案的symbol:- var_1.o:0000000000000004 C g_var1
- 這是一個common symbol。
- var_2.o:0000000000000004 C g_var1
- 這是一個common symbol。
- main.o: U g_var1
- 這是一個undefined symbol
- glvar:
glvar:0000000000601044 B g_var1
- 這是最後產生的executable object
- 這個symbol放在
.bss
,也就是未初始化的全域變數
- var_1.o:0000000000000004 C g_var1
分析 var1.c: int g_var = 2; var2.c: int g_var; 的結果
objdump -t
顯示object檔案的symbol table欄位資料:- var1.o: 0000000000000000 g O .data 0000000000000004 g_var1
- 這是一個全域變數物件,放在有初始值的
.data
section
- 這是一個全域變數物件,放在有初始值的
- var2.o: 0000000000000004 O COM 0000000000000004 g_var1
- 這是一個object,放在commom section
- main.o: 0000000000000000 UND 0000000000000000 g_var1
- 這是一個undefined symbol
- glvar: 0000000000601040 g O .data 0000000000000004 g_var1
- 這是一個全域變數物件,放在有初始值的
.data
section - 這是最後產生的executable object
- 這是一個全域變數物件,放在有初始值的
- var1.o: 0000000000000000 g O .data 0000000000000004 g_var1
nm -A
顯示object檔案的symbol:- var_1.o:0000000000000000 D g_var1
- 這個symbol是全域變數,並且放在有初始值的資料section
- var_2.o:0000000000000004 C g_var1
- 這是一個common symbol。
- main.o: U g_var1
- 這是一個undefined symbol
- glvar:0000000000601040 D g_var1
- 這是最後產生的executable object
- 這個symbol是全域變數,並且放在有初始值的資料section
- var_1.o:0000000000000000 D g_var1
分析 var1.c: int g_var2 = 2; var2.c: int g_var; 的結果
和上面的差別只有var1.o和var2.o的結果對調而已。
分析 var1.c: int g_var2 = 2; var2.c: int g_var = 3; 的結果
- 錯誤訊息: g_var1重複定義
1 2 3 4 5 6 7 8 9 |
|
objdump -t
顯示object檔案的symbol table欄位資料:- var1.o: 0000000000000000 g O .data 0000000000000004 g_var1
- 這是一個全域變數物件,放在有初始值的
.data
section
- 這是一個全域變數物件,放在有初始值的
- var2.o: 0000000000000000 g O .data 0000000000000004 g_var1
- 這是一個全域變數物件,放在有初始值的
.data
section
- 這是一個全域變數物件,放在有初始值的
- main.o: 0000000000000000 UND 0000000000000000 g_var1
- 這是一個undefined symbol
- var1.o: 0000000000000000 g O .data 0000000000000004 g_var1
nm -A
顯示object檔案的symbol:- var_1.o:0000000000000000 D g_var1
- 這個symbol是全域變數,並且放在有初始值的資料section
- var_2.o:0000000000000000 D g_var1
- 這個symbol是全域變數,並且放在有初始值的資料section
- main.o: U g_var1
- 這是一個undefined symbol
- var_1.o:0000000000000000 D g_var1
分析 var1.c: int g_var; var2.c: extern int g_var; 的結果
objdump -t
顯示object檔案的symbol table欄位資料:- var1.o: 0000000000000004 O COM 0000000000000004 g_var1
- 這是一個object,放在commom section
- var2.o: 沒有g_var1這個entry
- main.o: 0000000000000000 UND 0000000000000000 g_var1
- 這是一個undefined symbol
- glvar: 0000000000601040 g O .data 0000000000000004 g_var1
- 這是一個全域變數物件,放在有初始值的
.data
section - 這是最後產生的executable object
- 這是一個全域變數物件,放在有初始值的
- var1.o: 0000000000000004 O COM 0000000000000004 g_var1
nm -A
顯示object檔案的symbol:- var_1.o:0000000000000004 C g_var1
- 這是一個common symbol
- var_2.o: 沒有symbol
- main.o: U g_var1
- 這是一個undefined symbol
- glvar:0000000000601040 B g_var1
- 這是最後產生的executable object
- 這個symbol是全域變數,並且放在沒有初始值的資料section
- var_1.o:0000000000000004 C g_var1
分析 var1.c: int g_var = 2; var2.c: extern int g_var; 的結果
objdump -t
顯示object檔案的symbol table欄位資料:- var1.o: 0000000000000000 g O .data 0000000000000004 g_var1
- 這是一個全域變數物件,放在有初始值的
.data
section
- 這是一個全域變數物件,放在有初始值的
- var2.o: 沒有g_var1這個entry
- main.o: 0000000000000000 UND 0000000000000000 g_var1
- 這是一個undefined symbol
- glvar: 0000000000601040 g O .data 0000000000000004 g_var1
- 這是一個全域變數物件,放在有初始值的
.data
section - 這是最後產生的executable object
- 這是一個全域變數物件,放在有初始值的
- var1.o: 0000000000000000 g O .data 0000000000000004 g_var1
nm -A
顯示object檔案的symbol:- var_1.o:0000000000000000 D g_var1
- 這個symbol是全域變數,並且放在有初始值的資料section
- var_2.o: 沒有symbol
- main.o: U g_var1
- 這是一個undefined symbol
- glvar:0000000000601040 D g_var1
- 這是最後產生的executable object
- 這個symbol是全域變數,並且放在有初始值的資料section
- var_1.o:0000000000000000 D g_var1
分析 var1.c: int g_var; var2.c: extern int g_var = 3; 的結果
- 得到警告和錯誤:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
|
objdump -t
顯示object檔案的symbol table欄位資料:- var1.o: 0000000000000004 O COM 0000000000000004 g_var1
- 這是一個object,放在commom section
- var2.o: 0000000000000000 g O .data 0000000000000004 g_var1
- 這是一個全域變數物件,放在有初始值的
.data
sectio
- 這是一個全域變數物件,放在有初始值的
- main.o: 0000000000000000 UND 0000000000000000 g_var1
- 這是一個undefined symbol
- glvar: 0000000000601040 g O .data 0000000000000004 g_var1
- 這是一個全域變數物件,放在有初始值的
.data
section - 這是最後產生的executable object
- 這是一個全域變數物件,放在有初始值的
- var1.o: 0000000000000004 O COM 0000000000000004 g_var1
nm -A
顯示object檔案的symbol:- var_1.o:0000000000000000 D g_var1
- 這是一個common symbol
- var_2.o:0000000000000000 D g_var1
- 這個symbol是全域變數,並且放在有初始值的資料section
- main.o: U g_var1
- 這是一個undefined symbol
- glvar:0000000000601040 D g_var1
- 這是最後產生的executable object
- 這個symbol是全域變數,並且放在有初始值的資料section
- var_1.o:0000000000000000 D g_var1
分析 var1.c: int g_var = 2; var2.c: extern int g_var = 3; 的結果
- 得到警告和link錯誤:
1 2 3 4 5 6 7 8 9 10 11 12 |
|
objdump -t
顯示object檔案的symbol table欄位資料:- var1.o: 0000000000000000 g O .data 0000000000000004 g_var1
- 這是一個全域變數物件,放在有初始值的
.data
section
- 這是一個全域變數物件,放在有初始值的
- var2.o: 0000000000000000 g O .data 0000000000000004 g_var1
- 這是一個全域變數物件,放在有初始值的
.data
section
- 這是一個全域變數物件,放在有初始值的
- main.o: 0000000000000000 UND 0000000000000000 g_var1
- 這是一個undefined symbol
- var1.o: 0000000000000000 g O .data 0000000000000004 g_var1
nm -A
顯示object檔案的symbol:- var_1.o:0000000000000000 D g_var1
- 這個symbol是全域變數,並且放在有初始值的資料section
- var_2.o:0000000000000000 D g_var1
- 這個symbol是全域變數,並且放在有初始值的資料section
- main.o: U g_var1
- 這是一個undefined symbol
- var_1.o:0000000000000000 D g_var1
分析 var1.c: extern int g_var; var2.c: extern int g_var; 的結果
- link時發生錯誤
1 2 3 4 5 6 7 8 9 |
|
objdump -t
顯示object檔案的symbol table欄位資料:- var1.o: 沒有g_var這個 entry
- var2.o: 沒有g_var這個 entry
- main.o: 0000000000000000 UND 0000000000000000 g_var1
- 這是一個undefined symbol
nm -A
顯示object檔案的symbol:- var_1.o: 沒有symbol
- var_2.o: 沒有symbol
- main.o: U g_var1
- 這是一個undefined symbol
分析 var1.c: extern int g_var = 2; var2.c: extern int g_var; 的結果
- 得到警告:
1
|
|
objdump -t
顯示object檔案的symbol table欄位資料:- var1.o: 0000000000000000 g O .data 0000000000000004 g_var1
- 這是一個全域變數物件,放在有初始值的
.data
section
- 這是一個全域變數物件,放在有初始值的
- var2.o: 沒有g_var這個 entry
- main.o: 0000000000000000 UND 0000000000000000 g_var1
- 這是一個undefined symbol
- var1.o: 0000000000000000 g O .data 0000000000000004 g_var1
nm -A
顯示object檔案的symbol:- var_1.o:0000000000000000 D g_var1
- 這個symbol是全域變數,並且放在有初始值的資料section
- var_2.o: 沒有symbol
- main.o: U g_var1
- 這是一個undefined symbol
- var_1.o:0000000000000000 D g_var1
分析 var1.c: extern int g_var2; var2.c: extern int g_var = 3; 的結果
和上面的差別只有var1.o和var2.o的結果對調而已。
分析 var1.c: extern int g_var2 = 2; var2.c: extern int g_var = 3; 的結果
- 得到警告和錯誤:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
|
objdump -t
顯示object檔案的symbol table欄位資料:- var1.o: 0000000000000000 g O .data 0000000000000004 g_var1
- 這是一個全域變數物件,放在有初始值的
.data
section
- 這是一個全域變數物件,放在有初始值的
- var2.o: 0000000000000000 g O .data 0000000000000004 g_var1
- 這是一個全域變數物件,放在有初始值的
.data
section
- 這是一個全域變數物件,放在有初始值的
- main.o: 0000000000000000 UND 0000000000000000 g_var1
- 這是一個undefined symbol
- var1.o: 0000000000000000 g O .data 0000000000000004 g_var1
nm -A
顯示object檔案的symbol:- var_1.o:0000000000000000 D g_var1
- 這個symbol是全域變數,並且放在有初始值的資料section
- var_2.o:0000000000000000 D g_var1
- 這個symbol是全域變數,並且放在有初始值的資料section
- main.o: U g_var1
- 這是一個undefined symbol
- var_1.o:0000000000000000 D g_var1
結論
- common symbol表示object檔案已經紀錄下這個symbol,先放在common section。最後link的時候才會決定要怎麼處理。 出處
gcc -c
產生的object檔案中未初始化的全域變數會放在common section而不是.bss
section,原因是link時期其他的object可能有相同名稱的symbol,而這些相同的symbol可能有初始化也可能沒有。而linker需要全盤了解後再做出決定要放在.bss
還是.data
section。- 最後link出來的executable object檔案不會有common symbol
- 多個檔案宣告同樣名稱的全域變數時,編譯時會依下面的情況處理
- 超過兩個檔案有設初始值,噴錯誤
- 都沒設定初始值,產生的object檔案放common section。link時放
.bss
section。 - 一個檔案有設定初始值,其他沒有設定初始值的情形
- 有設定初始值那個檔案的object檔案變數放在
.data
- 其他沒有設定初始值有設定初始值那個檔案的object檔案變數放在common section
- link時放
.data
section
- 有設定初始值那個檔案的object檔案變數放在
- 使用
extern
全域變數的時候- 程式碼沒用到該全域變數在未link前的object 檔案不會有該symbol
- 程式碼用到該全域變數在未link前的object 檔案該symbol為undefined
- 程式碼給初始值會產生警告,並且會被新增到該object 檔案的symbol table,以至於其他檔案宣告該全域變數會產生重複定義的錯誤。