淺談C語言中的強符號、弱符號、強引用和弱引用
首先我表示很悲劇,在看《程序員的自我修養(yǎng)--鏈接、裝載與庫》之前我竟不知道C有強符號、弱符號、強引用和弱引用。在看到3.5.5節(jié)弱符號和強符號時,我感覺有些困惑,所以寫下此篇,希望能和同樣感覺的朋友交流也希望高人指點。
首先我們看一下書中關于它們的定義。
引入場景:(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++來說,編譯器默認函數(shù)和初始化的全局變量為強符號。
弱符號:接上文,為初始化的全局變量為弱符號。
編譯器關于強弱符號的規(guī)則有:(1)強符號不允許多次定義,但強弱可以共存;(2)強弱共存時,強覆蓋弱;(3)都是弱符號時,選擇占用空間最大的,如選擇 double類型的而不選擇int類型的。
由以上定義所以有我之前沒有想到的場景:
代碼a.c:
1 int i = 2;
代碼b.c:
#include<stdio.h>
int i;
int main(int argc, char** argv)
{
printf("i = %d\n", i);
return 0;
}
編譯文件a和b并鏈接,結果輸出i為2而不是0。
并且在同一個文件中定義但未初始化兩個相同的變量不會報錯,只有在使用變量時才會報錯。
對于GCC編譯器來說,還允許使用__attribute__((weak))來將強符號定義為弱符號,所已有
代碼c.c
#include<stdio.h>
__attribute__((weak)) int i = 1;
int main(int argc, char** argv)
{
printf("i = %d\n", i);
return 0;
}
結果i的輸出仍未2而不是1。
那么對于函數(shù)而言是不是也這樣呢?先不看函數(shù),而是先看由強弱符號而進一步引入的強弱引用。書中關于強弱引用的概述是對于強引用若未定義則鏈接時肯定會報錯,而對于弱引用則不會報錯,鏈接器默認其為0(這一點對于函數(shù)好理解,即函數(shù)符號所代表入口地址為0;對于變量就要注意了,既然是引用那自然就是地址了,所以同函數(shù)一樣變量的地址為0而不是變量的值為0)。此時對于強弱引用是不是還沒有什么明確的概念呢?到底什么是引用?引用和符號又是什么關系?這里我說一下我的理解(歡迎指正),在定義和聲明處指定的函數(shù)名、變量名即為對應的符號,而在代碼其他處調用函數(shù)或使用變量時,則把函說明和變量名看作引用,這樣一來符號和引用在代碼層面上其實就是一個東西,只是根據環(huán)境而叫法不同而已。那么強符號對應強引用,弱符號對應弱引用。
有上面的強弱引用的特點可看出,當一個函數(shù)為弱引用時,不管這個函數(shù)有沒有定義,鏈接時都不會報錯,而且我們可以根據判斷函數(shù)名是否為0來決定是否執(zhí)行這個函數(shù)。這樣一來,包含這些函數(shù)的庫就可以以模塊、插件的形式和我們的引用組合一起,方便使用和卸載,并且由于強符號可以覆蓋弱符號和強弱符號與強弱引用的關系可知,我們自己定義函數(shù)可以覆蓋庫中的函數(shù),多么美妙。
先看根據條件判斷是否執(zhí)行函數(shù):
代碼d.c
#include<stdio.h>
void func()
{
printf("func()#1\n");
}
代碼e.c
#include<stdio.h>
__attribute__((weak)) void func();
int main(int argc, char** argv)
{
if (func)
func();
return 0;
}
編譯d.c,cc -c d.c 輸出d.o;編譯e.c并鏈接d.o,cc d.o e.c -o e輸出可執(zhí)行文件e,運行e正常執(zhí)行函數(shù)func。編譯e.c但不鏈接d.o,此時并不會報錯,只不過func不會執(zhí)行,因為沒有它的定義所以if(func)為假。
再看函數(shù)覆蓋:
代碼f.c
#include<stdio.h>
__attribute__((weak)) void func()
{
printf("func()#1\n");
}
代碼g.c
#include<stdio.h>
void func()
{
printf("func()#2\n");
}
int main(int argc, char** argv)
{
func();
return 0;
}
~
編譯鏈接,結構輸出"func()#2"。
以上可以說明函數(shù)和變量是保持一致的,其實對應變量也可以像使用函數(shù)那樣先判斷再使用,只不過不是判斷變量的值而是變量的地址,如
代碼v1.c
int i = 2;
代碼v2.c
#include<stdio.h>
__attribute__((weak)) extern int i;
int main(int argc, char** argv)
{
if (&i)
printf("i = %d\n", i);
return 0;
}
~
編譯并鏈接v1時,輸出2;編譯但不鏈接v1時無輸出。這樣做時要分清定義和聲明的區(qū)別,__attribute__((weak)) int i 是定義變量并轉換為弱符號,這樣i是分配了空間的,而__attribute__((weak)) extern int i 則將原來定義的變量i由強符號轉換為弱符號,導致使用i時不是強引用而是弱引用。不過雖然變量可以這么做但沒有函數(shù)那樣有意義。
上面關于強弱引用仍舊使用的是GCC提供的__attribute__((weak)),而書中還提到了__attribute__((weakref)),后者貌似更能體現(xiàn)“引用”這一關鍵詞。而我之所以使用前者來介紹強弱引用,是因為我對關于強弱符號與強弱引用對應關系的理解。關于__attribute__((weakref))的使用方法,這里介紹一種(兩者都有不同的使用方法)。
代碼a.c
#include<stdio.h>
void bar()
{
printf("foo()\n");
}
代碼b.c
#include<stdio.h>
static void foo() __attribute__((weakref("bar")));
int main(int argc, char** argv)
{
if (foo)
foo();
return 0;
}
注意函數(shù)foo的static修飾符,沒有的話會報錯,這樣將函數(shù)foo限制在只有本文件內可使用。
好了,夜已深,寫的有點凌亂,我也凌亂了。
欄 目:C語言
本文地址:http://mengdiqiu.com.cn/a1/Cyuyan/3201.html
您可能感興趣的文章
- 04-02c語言函數(shù)調用后清空內存 c語言調用函數(shù)刪除字符
- 04-02c語言的正則匹配函數(shù) c語言正則表達式函數(shù)庫
- 04-02func函數(shù)+在C語言 func函數(shù)在c語言中
- 04-02c語言中對數(shù)函數(shù)的表達式 c語言中對數(shù)怎么表達
- 04-02c語言用函數(shù)寫分段 用c語言表示分段函數(shù)
- 04-02c語言編寫函數(shù)冒泡排序 c語言冒泡排序法函數(shù)
- 04-02c語言沒有round函數(shù) round c語言
- 04-02c語言分段函數(shù)怎么求 用c語言求分段函數(shù)
- 04-02C語言中怎么打出三角函數(shù) c語言中怎么打出三角函數(shù)的值
- 04-02c語言調用函數(shù)求fibo C語言調用函數(shù)求階乘


閱讀排行
本欄相關
- 04-02c語言函數(shù)調用后清空內存 c語言調用
- 04-02func函數(shù)+在C語言 func函數(shù)在c語言中
- 04-02c語言的正則匹配函數(shù) c語言正則表達
- 04-02c語言用函數(shù)寫分段 用c語言表示分段
- 04-02c語言中對數(shù)函數(shù)的表達式 c語言中對
- 04-02c語言編寫函數(shù)冒泡排序 c語言冒泡排
- 04-02c語言沒有round函數(shù) round c語言
- 04-02c語言分段函數(shù)怎么求 用c語言求分段
- 04-02C語言中怎么打出三角函數(shù) c語言中怎
- 04-02c語言調用函數(shù)求fibo C語言調用函數(shù)求
隨機閱讀
- 01-10delphi制作wav文件的方法
- 01-11ajax實現(xiàn)頁面的局部加載
- 01-11Mac OSX 打開原生自帶讀寫NTFS功能(圖文
- 01-10SublimeText編譯C開發(fā)環(huán)境設置
- 08-05DEDE織夢data目錄下的sessions文件夾有什
- 04-02jquery與jsp,用jquery
- 08-05織夢dedecms什么時候用欄目交叉功能?
- 01-10C#中split用法實例總結
- 08-05dedecms(織夢)副欄目數(shù)量限制代碼修改
- 01-10使用C語言求解撲克牌的順子及n個骰子