最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

舉例講解C語(yǔ)言鏈接器的符號(hào)解析機(jī)制

 更新時(shí)間:2016年05月31日 18:45:12   作者:wudaijun  
鏈接器的工作主要分為兩個(gè)階段:符號(hào)解析和重定位,符號(hào)解析的功能是將每個(gè)模塊符號(hào)引用綁定到一個(gè)確切的符號(hào)定義,這里我們就來(lái)舉例講解C語(yǔ)言鏈接器的符號(hào)解析機(jī)制

1. 符號(hào)分類
(1)全局符號(hào):非靜態(tài)全局變量,非靜態(tài)函數(shù)
(2)外部符號(hào):定義于其它模塊,而被本模塊引用的全局變量和函數(shù)
(3)本地符號(hào):靜態(tài)變量(包括全局和局部),靜態(tài)函數(shù)
對(duì)于靜態(tài)局部變量,編譯器會(huì)為其生成唯一的名字。如x.fun1,x.fun2。本地符號(hào)對(duì)鏈接器來(lái)說(shuō)是不可見(jiàn)的。
2. 符號(hào)決議
當(dāng)編譯器遇到一個(gè)不是本模塊定義的符號(hào)時(shí),會(huì)假設(shè)該函數(shù)由其它模塊定義,并生成一個(gè)鏈接器符號(hào)表?xiàng)l目,交由鏈接器處理。如果鏈接器在它的任何輸入模塊都沒(méi)有找到該符號(hào),會(huì)給出一個(gè)類似undefined reference to 'xxx'的鏈接錯(cuò)誤。而如果鏈接器在輸入模塊中找到了一個(gè)以上的外部符號(hào)定義,這個(gè)時(shí)候就需要鏈接器進(jìn)行符號(hào)決議,鏈接器對(duì)多個(gè)外部符號(hào)定義可能并不報(bào)錯(cuò)甚至警告,而是按照它的規(guī)則去選擇其中一個(gè)符號(hào)定義。
鏈接器將各個(gè)模塊輸出的全局符號(hào),分類為強(qiáng)符號(hào)和弱符號(hào):
(1)強(qiáng)符號(hào):函數(shù)和已初始化的全局變量
(2)弱符號(hào):為初始化全局變量
根據(jù)強(qiáng)弱符號(hào)的定義,鏈接器按照下面的規(guī)則處理多重定義的符號(hào):
規(guī)則1:不允許有多個(gè)強(qiáng)符號(hào)定義
規(guī)則2:如果有一個(gè)強(qiáng)符號(hào)和多個(gè)弱符號(hào),那么選擇強(qiáng)符號(hào)
規(guī)則3:如果有多個(gè)弱符號(hào),那么從這些弱符號(hào)中選擇sizeof大的那個(gè),如果大小相同,則選擇先鏈接的那個(gè)
上面的規(guī)則是很多鏈接錯(cuò)誤的根源,因?yàn)榫幾g器在決議時(shí)可能默默地替你作出了決定,你并不知曉。根據(jù)上面的規(guī)則,可以引出下面幾個(gè)經(jīng)典例子:
例1:

// in lib1.c
int x;
void f()
{
  x = 1235;
}

// in main1.c
#include<stdio.h>
void f(void);

int x = 1234;

int main(void)
{
  f();
  printf("x=%d\n", x);
  return 0;
}

上面的代碼中,main函數(shù)printf輸出: x=1235。因?yàn)殒溄悠魍ㄟ^(guò)規(guī)則2決議符號(hào)x的定義為main.c中的強(qiáng)符號(hào)定義,而lib.c的作者并不知情,他對(duì)x的使用和修改影響到了main.c。這種交互修改,相互影響將會(huì)很復(fù)雜,因?yàn)榇蠹叶家詾樽约涸谧鰧?duì)的事情,在用對(duì)的變量。而整個(gè)決議過(guò)程,鏈接器悄無(wú)聲息地完成了。
例2:

// in lib2.c
double x;
void f()
{
  x = -0.0;
}

// in main2.c
#include<stdio.h>
void f(void);

int x = 1234;
int y = 1235;

int main()
{
  f();
  printf("x=0x%x y=0x%x \n", x, y);
  return 0;
}

這種情況下,程序得到輸出: x=0x0 y=0x80000000,而鏈接器(gcc ld)也終于給出一條警告:

復(fù)制代碼 代碼如下:

ld: warning: tentative definition of '_x' with size 8 from 'obj/Debug/lib2.o' is being replaced by real definition of smaller size 4 from 'obj/Debug/main2.o'


鏈接器決議的是符號(hào)地址,而相鄰的全局變量可能在.data段中的內(nèi)存地址也相鄰,因此也就引發(fā)了更復(fù)雜的問(wèn)題。這一點(diǎn)和棧溢出很像,但是比棧溢出更復(fù)雜,因?yàn)閱?wèn)題出在多個(gè)模塊之間,而不是在一個(gè)函數(shù)內(nèi)部。
例3:

// in lib3.c
struct
{
  int a;
  int b;
} x;

void f()
{
  x.a = 123;
  x.b = 456;
  printf("in f(): sizeof(x)=%d, (&x)=0x%08x\n", sizeof(x), &x);
}

// in main3.c
#include<stdio.h>
void f(void);

int x;
int y;

int main()
{
  f();
  printf("in main(): sizeof(x)=%d, (&x)=0x%08x, (&x)=0x%08x, x=%d,y=%d \n", sizeof(x), &x, &y, x, y);
  return 0;
}

程序輸出:

in f(): sizeof(x)=8, (&x)=0x02489018
in main(): sizeof(x)=4, (&x)=0x02489018, (&y)=0x02489020, x=123,y=0

始終記住,外部符號(hào)決議的是地址,因此無(wú)論lib3.c和main3.c中,符號(hào)x地址都是唯一的,無(wú)論其被定義了幾次。其次sizeof是編譯器決議,與鏈接無(wú)關(guān),編譯器只看得到本模塊的定義或聲明。最后,由于符號(hào)x決議到lib3.c中的x,其size是8,因此main3.c中的y的地址比x大8,這是由鏈接器將lib3.o和main3.o合并后填入可執(zhí)行文件的.data段的。因此y是無(wú)關(guān)變量,被初始化為0,注意和例2的區(qū)別。
3. 總結(jié)
由于符號(hào)決議容易引發(fā)的種種問(wèn)題,我們?cè)趯慍的時(shí)候應(yīng)注意:
盡量用static屬性隱藏變量和函數(shù)在模塊內(nèi)的聲明,就像在C++中盡量用private保護(hù)類私有成員一樣。
少定義弱符號(hào),盡量初始化全局變量,這樣鏈接器會(huì)根據(jù)規(guī)則1給出多個(gè)符號(hào)定義的錯(cuò)誤。
為鏈接器設(shè)置必要選項(xiàng),如gcc的 -fno-common,這樣在遇到多重符號(hào)定義時(shí),鏈接器會(huì)給出警告。
4. C++的符號(hào)決議
C++并不支持強(qiáng)弱符號(hào)同時(shí)存在,所有符號(hào)都只能有一個(gè)定義(函數(shù)重載通過(guò)改寫函數(shù)符號(hào)來(lái)確保其唯一),因此在很大程度上避免了C中的鏈接器困擾。

相關(guān)文章

  • C語(yǔ)言中宏定義的妙用方法

    C語(yǔ)言中宏定義的妙用方法

    今天小編就為大家分享一篇關(guān)于C語(yǔ)言中宏定義的妙用方法,小編覺(jué)得內(nèi)容挺不錯(cuò)的,現(xiàn)在分享給大家,具有很好的參考價(jià)值,需要的朋友一起跟隨小編來(lái)看看吧
    2018-12-12
  • Objective-C的內(nèi)省(Introspection)用法小結(jié)

    Objective-C的內(nèi)省(Introspection)用法小結(jié)

    這篇文章主要介紹了Objective-C的內(nèi)省(Introspection)用法,這是面向?qū)ο笳Z(yǔ)言和環(huán)境的一個(gè)強(qiáng)大特性,需要的朋友可以參考下
    2014-07-07
  • C++?Cartographer源碼中關(guān)于MapBuilder的聲明與構(gòu)造

    C++?Cartographer源碼中關(guān)于MapBuilder的聲明與構(gòu)造

    這篇文章主要介紹了C++?Cartographer源碼中關(guān)于MapBuilder的聲明與構(gòu)造,前面已經(jīng)談到了Cartographer中添加軌跡的方法和傳感器的數(shù)據(jù)流動(dòng)走向。在添加軌跡的時(shí)候,除了添加位姿估計(jì)器還有采樣器,訂閱回調(diào)函數(shù)之外,最重要的是通過(guò)map_builder_bridge添加了一條軌跡
    2023-03-03
  • C++實(shí)現(xiàn)LeetCode(55.跳躍游戲)

    C++實(shí)現(xiàn)LeetCode(55.跳躍游戲)

    這篇文章主要介紹了C++實(shí)現(xiàn)LeetCode(55.跳躍游戲),本篇文章通過(guò)簡(jiǎn)要的案例,講解了該項(xiàng)技術(shù)的了解與使用,以下就是詳細(xì)內(nèi)容,需要的朋友可以參考下
    2021-07-07
  • C語(yǔ)言的abs()函數(shù)和div()函數(shù)你了解嗎

    C語(yǔ)言的abs()函數(shù)和div()函數(shù)你了解嗎

    這篇文章主要為大家詳細(xì)介紹了C語(yǔ)言的abs()函數(shù)和div()函數(shù),文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下,希望能夠給你帶來(lái)幫助
    2022-02-02
  • Windows安裝Qt6.4.2及簡(jiǎn)單驗(yàn)證

    Windows安裝Qt6.4.2及簡(jiǎn)單驗(yàn)證

    本文主要介紹了Windows安裝Qt6.4.2及簡(jiǎn)單驗(yàn)證,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2023-02-02
  • C語(yǔ)言單值二叉樹真題講解

    C語(yǔ)言單值二叉樹真題講解

    單值二叉樹你可能之前沒(méi)見(jiàn)過(guò),如果二叉樹每個(gè)節(jié)點(diǎn)都具有相同的值,那么該二叉樹就是單值二叉樹,讓我們通過(guò)一個(gè)真題來(lái)深刻了解它吧
    2022-04-04
  • C語(yǔ)言中函數(shù)聲明與調(diào)用問(wèn)題

    C語(yǔ)言中函數(shù)聲明與調(diào)用問(wèn)題

    以下是對(duì)C語(yǔ)言中的函數(shù)聲明與調(diào)用進(jìn)行了詳細(xì)的分析介紹,需要的朋友可以過(guò)來(lái)參考下
    2013-08-08
  • C語(yǔ)言中關(guān)于scanf讀取緩存區(qū)的問(wèn)題

    C語(yǔ)言中關(guān)于scanf讀取緩存區(qū)的問(wèn)題

    scanf()函數(shù)是通用終端格式化輸入函數(shù),它從標(biāo)準(zhǔn)輸入設(shè)備(鍵盤) 讀取輸入的信息,接下來(lái)通過(guò)本文給大家介紹C語(yǔ)言中關(guān)于scanf讀取緩存區(qū)的問(wèn)題,需要的朋友一起看看吧
    2021-09-09
  • C++棧的數(shù)組實(shí)現(xiàn)代碼

    C++棧的數(shù)組實(shí)現(xiàn)代碼

    這篇文章主要介紹了C++棧的數(shù)組實(shí)現(xiàn)方式,本文結(jié)合實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2022-05-05

最新評(píng)論

潮州市| 富源县| 桐乡市| 高雄县| 吉林市| 化隆| 成都市| 海阳市| 托克托县| 临城县| 株洲县| 二手房| 博爱县| 和平县| 文化| 蛟河市| 盘锦市| 南漳县| 衡山县| 冷水江市| 鄂伦春自治旗| 隆化县| 昌图县| 阿巴嘎旗| 璧山县| 开江县| 碌曲县| 新晃| 怀仁县| 中山市| 霍州市| 宣武区| 聂荣县| 菏泽市| 新兴县| 调兵山市| 当雄县| 镇赉县| 嘉义市| 灵武市| 平阴县|