首先我表示很悲劇,在看《程式設計師的自我修養--鏈結、裝載與庫》之前我竟不知道c有強符號、弱符號、強引用和弱引用。在看到3.5.5節弱符號和強符號時,我感覺有些困惑,所以寫下此篇,希望能和同樣感覺的朋友交流也希望高人指點。
首先我們看一下書中關於它們的定義。
引入場景:(1)檔案a中定義並初始化變數i(int i = 1), 檔案b中定義並初始化變數i(int i = 2)。編譯鏈結a、b時會報錯b.o:(.data+0x0): multiple definition of `i';a.o:(.data+0x0): multiple definition of `i'。(2)在檔案c中定義並初始化兩個變數i(int i = 1; int i = 2), 編譯鏈結時會報錯c
.c:2:5: error: redefinition of 『i』; c.c:1:5: note: previous definition of 『i』 was here。
強符號:像場景中這樣的符號定義被稱為強符號,對於c/c++來說,編譯器預設函式和初始化的全域性變數為強符號。
弱符號:接上文,為初始化的全域性變數為弱符號。
編譯器關於強弱符號的規則有:(1)強符號不允許多次定義,但強弱可以共存;(2)強弱共存時,強覆蓋弱;(3)都是弱符號時,選擇占用空間最大的,如選擇 double型別的而不選擇int型別的。
由以上定義所以有我之前沒有想到的場景:
**a.c:
1**b.c:int i = 2;
#includeint編譯檔案a和b並鏈結,結果輸出i為2而不是0。i;int main(int argc, char**argv)
並且在同乙個檔案中定義但未初始化兩個相同的變數不會報錯,只有在使用變數時才會報錯。
對於gcc編譯器來說,還允許使用__attribute__((weak))來將強符號定義為弱符號,所已有
**c.c
1 #include2結果i的輸出仍未2而不是1。3 __attribute__((weak)) int i = 1;4
5int main(int argc, char**argv)
6
那麼對於函式而言是不是也這樣呢?先不看函式,而是先看由強弱符號而進一步引入的強弱引用。書中關於強弱引用的概述是對於強引用若未定義則鏈結時肯定會報錯,而對於弱引用則不會報錯,鏈結器預設其為0(這一點對於函式好理解,即函式符號所代表入口位址為0;對於變數就要注意了,既然是引用那自然就是位址了,所以同函式一樣變數的位址為0而不是變數的值為0)。此時對於強弱引用是不是還沒有什麼明確的概念呢?到底什麼是引用?引用和符號又是什麼關係?這裡我說一下我的理解(歡迎指正),在定義和宣告處指定的函式名、變數名即為對應的符號,而在**其他處呼叫函式或使用變數時,則把函說明和變數名看作引用,這樣一來符號和引用在**層面上其實就是乙個東西,只是根據環境而叫法不同而已。那麼強符號對應強引用,弱符號對應弱引用。
有上面的強弱引用的特點可看出,當乙個函式為弱引用時,不管這個函式有沒有定義,鏈結時都不會報錯,而且我們可以根據判斷函式名是否為0來決定是否執行這個函式。這樣一來,包含這些函式的庫就可以以模組、外掛程式的形式和我們的引用組合一起,方便使用和解除安裝,並且由於強符號可以覆蓋弱符號和強弱符號與強弱引用的關係可知,我們自己定義函式可以覆蓋庫中的函式,多麼美妙。
先看根據條件判斷是否執行函式:
**d.c
1 #include2**e.c3void
func()
4
1 #include2編譯d.c,cc -c d.c 輸出d.o;編譯e.c並鏈結d.o,cc d.o e.c -o e輸出可執行檔案e,執行e正常執行函式func。編譯e.c但不鏈結d.o,此時並不會報錯,只不過func不會執行,因為沒有它的定義所以if(func)為假。3 __attribute__((weak)) void
func();45
int main(int argc, char**argv)
6
再看函式覆蓋:
**f.c
1 #include2**g.c3 __attribute__((weak)) void
func()
4
1 #include2編譯鏈結,結構輸出"func()#2"。3void
func()47
8int main(int argc, char**argv)
913 ~
以上可以說明函式和變數是保持一致的,其實對應變數也可以像使用函式那樣先判斷再使用,只不過不是判斷變數的值而是變數的位址,如
**v1.c
int i = 2;**v2.c
1 #include2編譯並鏈結v1時,輸出2;編譯但不鏈結v1時無輸出。這樣做時要分清定義和宣告的區別,__attribute__((weak)) int i 是定義變數並轉換為弱符號,這樣i是分配了空間的,而__attribute__((weak)) extern int i 則將原來定義的變數i由強符號轉換為弱符號,導致使用i時不是強引用而是弱引用。不過雖然變數可以這麼做但沒有函式那樣有意義。3 __attribute__((weak)) extern
inti;45
int main(int argc, char**argv)
611 ~
1 #include2**b.c3void
bar()
4
1 #include2注意函式foo的static修飾符,沒有的話會報錯,這樣將函式foo限制在只有本檔案內可使用。3static
void foo() __attribute__((weakref("
bar"
)));45
int main(int argc, char**argv)
6
C語言中的強符號和弱符號
c語言中的強符號和弱符號 c語言真的很奇怪,各種你想不到的問題都會出現,但是仔細分析這個問題,無不很有道理,這些都不是c語言的漏洞,而是這門語言的強大之處。首先介紹一下強符號和弱符號。當多個c檔案需要一起編譯執行的時候,就會有強弱符號的問題。為什麼會有多個c檔案一起編譯呢?這就的說起鏈結linkin...
C語言中的強符號與弱符號
參考 程式設計師的自我修養 參考 c語言中的強符號與弱符號 main.c int a 100 int main other.c int a 10 編譯 gcc main.c other.c編譯結果 ld 1 duplicate symbol for architecture x86 64 clang...
C語言中的強符號與弱符號問題
弱符號與強符號 對於c c 語言來說,編譯器預設函式和初始化了的全域性變數為強符號,未初始化的全域性變數為弱符號。我們也可以通過gcc的 attribute weak 將乙個強符號變為弱符號。強符號和弱符號都是針對定義來說的,不是針對符號的引用。extern int ext int weak 弱符號...