C語(yǔ)言?超詳細(xì)講解鏈接器
1 什么是鏈接器
典型的鏈接器把由編譯器或匯編器生成的若干個(gè)目標(biāo)模塊,整合成一個(gè)被稱(chēng)為載入模塊或可執(zhí)行文件的實(shí)體–該實(shí)體能夠被操作系統(tǒng)直接執(zhí)行。

鏈接器通常把目標(biāo)模塊看成是由一組外部對(duì)象組成的。每個(gè)外部對(duì)象代表著機(jī)器內(nèi)存中的某個(gè)部分,并通過(guò)一個(gè)外部名稱(chēng)來(lái)識(shí)別。因此,==程序中的每個(gè)函數(shù)和每個(gè)外部變量,如果沒(méi)有被聲明為static,就都是一個(gè)外部對(duì)象。==某些C編譯器會(huì)對(duì)靜態(tài)函數(shù)和靜態(tài)變量的名稱(chēng)做一定改變,將它們也作為外部對(duì)象。由于經(jīng)過(guò)了“名稱(chēng)修飾”,因此它們不會(huì)與其它源程序文件中的同名函數(shù)或同名變量發(fā)生命名沖突。
2 聲明與定義
extern int a;
上面的這段代碼并不是對(duì)a的定義,而是說(shuō)明a是一個(gè)外部整型變量。
注意:引入之后,假如引入的位置在函數(shù)之外,就相當(dāng)于在那個(gè)位置定義了全局變量,同樣遵循局部變量?jī)?yōu)先原則,如果引入位置在某個(gè)函數(shù)之內(nèi),就相當(dāng)于是一個(gè)局部變量,作用域與那個(gè)地方定義的局部變量相類(lèi)似,此處討論聲明周期沒(méi)有任何意義。
int a; extern int a;
上面的這兩條語(yǔ)句既可以是在同一個(gè)源文件中,也可以位于程序的不同源文件之中。
==注意:每個(gè)外部變量只能定義一次。==如果外部變量的多個(gè)定義各指定一個(gè)初始值,例如:
int a = 7;
出現(xiàn)在一個(gè)源文件中,而
int a = 9;
出現(xiàn)在另一個(gè)源文件中,大多數(shù)系統(tǒng)都會(huì)拒絕接收該程序。但是,如果一個(gè)外部變量在多個(gè)源文件中定義卻沒(méi)有指定初始值,那么**某些系統(tǒng)會(huì)接受這個(gè)程序,而另外一些系統(tǒng)則不會(huì)接受。**所以,每個(gè)外部變量必須只定義一次。
3 命名沖突
3.1 命名沖突
如果在兩個(gè)不同的源文件中都包括了定義
int a;
那么它要么表示程序錯(cuò)誤(如果鏈接器進(jìn)制外部變量重復(fù)命名的話),要么在兩個(gè)源文件中共享a的同一個(gè)實(shí)例(無(wú)論兩個(gè)源文件中的外部變量是否應(yīng)該被共享)。即使其中a的一個(gè)定義是出現(xiàn)在系統(tǒng)提供的庫(kù)文件中,也仍然進(jìn)行同樣的處理。
3.2 static修飾符
static int a;
static修飾a之后,a的作用域?qū)⒈幌拗圃谝粋€(gè)源文件中,對(duì)于其它源文件,a是不可見(jiàn)的,且無(wú)法再被extern所引用,當(dāng)然,static也適用于函數(shù)。使用static之后,我們就可以在其它的源文件中定義和這個(gè)已經(jīng)被static修飾后的同名的變量或者函數(shù)。
4 形參、實(shí)參、返回值
如果我們使用的函數(shù)并未進(jìn)行聲明,但是已經(jīng)在后面進(jìn)行了定義,此時(shí)會(huì)默認(rèn)函數(shù)返回類(lèi)型為int型,這會(huì)造成極其嚴(yán)重的后果。
使用的函數(shù)如果在使用之前并未定義或者可能在其他的文件中,那么就要進(jìn)行聲明,函數(shù)聲明的目的就是告知編譯器函數(shù)的返回值的類(lèi)型。
注意:如果一個(gè)函數(shù)沒(méi)有float、short、或者char類(lèi)型的參數(shù),在函數(shù)聲明中完全可以省略掉參數(shù)類(lèi)型的說(shuō)明(注意,函數(shù)定義中不能省略參數(shù)類(lèi)型的說(shuō)明)。這種做法依賴(lài)于調(diào)用者能夠提供數(shù)目正確且類(lèi)型恰當(dāng)?shù)膶?shí)參。這里,“恰當(dāng)”并不意味著“等同”:float類(lèi)型的參數(shù)會(huì)自動(dòng)轉(zhuǎn)換為double類(lèi)型,short或者char類(lèi)型的參數(shù)會(huì)自動(dòng)轉(zhuǎn)換為int類(lèi)型。
在ANSI C標(biāo)準(zhǔn)發(fā)布之前,常常會(huì)有下面的這種聲明和定義函數(shù)的方式:
int isvowel();//聲明函數(shù)的方式
int isvowel(c)
char c;
{
return c =='a' ;
}
實(shí)際上,上面這種寫(xiě)法與下面這種寫(xiě)法是等價(jià)的:
int isvowel(int i)
{
char c;
return c=='a';
}
上述兩種方式在VS2019中都是支持的。
看下面的例子:
#include<stdio.h>
int main()
{
int i;
char c;
for (i = 0; i < 5; i++)
{
scanf("%d", &c);
printf("%d ", i);
}
printf("\n");
return 0;
}
表面上,這個(gè)程序從標(biāo)準(zhǔn)輸入設(shè)備讀入5個(gè)數(shù),在標(biāo)準(zhǔn)輸出設(shè)備設(shè)備上寫(xiě)5個(gè)數(shù):
0 1 2 3 4
實(shí)際上,這個(gè)程序并不一定得到上面的結(jié)果。例如,在某個(gè)編譯器上,它的輸出是(當(dāng)然,在VS2019環(huán)境下程序會(huì)崩潰,因?yàn)榉欠ㄐ薷牧藘?nèi)存空間)
0 0 0 0 0 1 2 3 4
為什么呢?問(wèn)題的關(guān)鍵在于,這里的c被聲明為char類(lèi)型,而不是int類(lèi)型。如果程序要求scanf讀入一個(gè)整數(shù),應(yīng)該傳遞給他一個(gè)指向整數(shù)的指針。而程序中scanf函數(shù)得到的卻是一個(gè)指向字符的指針,scanf函數(shù)并不能分辨這種情況,它只是將這個(gè)指向字符的指針作為指向整數(shù)的指針而接受,并且在指針指向的位置存儲(chǔ)一個(gè)整數(shù)。因?yàn)檎麛?shù)所占的存儲(chǔ)空間要大于字符所占的存儲(chǔ)空間,所以字符c附近的內(nèi)存被覆蓋。
字符c附近的內(nèi)存中存儲(chǔ)的內(nèi)容是由編譯器決定的,在本例中它所存放的是整數(shù)i的低端部分。因此,每次讀入一個(gè)數(shù)值到c時(shí),都會(huì)將i的低端部分覆蓋為0,而i的高端部分本來(lái)就是0,相當(dāng)于i每次被重新設(shè)置為0,循環(huán)將一直進(jìn)行。當(dāng)?shù)竭_(dá)文件的結(jié)束位置后,scanf函數(shù)不再試圖讀入新的值到c。這時(shí),i才可以正常的運(yùn)行,最后終止循環(huán)。
5 檢查外部類(lèi)型
注意:保證一個(gè)特定類(lèi)型的所有外部定義在每個(gè)目標(biāo)模塊中都有相同的類(lèi)型,“相同的類(lèi)型”也應(yīng)該是嚴(yán)格意義上的相同。
例如,在一個(gè)文件中包含定義:
char filename[] = "/etc/passwd";
而在另一個(gè)文件中包含聲明:
extern char *filename;
在定義時(shí),filename是一個(gè)字符數(shù)組的名稱(chēng)。盡管在一個(gè)語(yǔ)句中引用filename的值將得到指向該數(shù)組起始元素的指針,但是filename的類(lèi)型是”字符數(shù)組“,而不是字符指針。在第二個(gè)聲明中,filename被確定為一個(gè)指針。這兩種方式使用存儲(chǔ)空間的方式是不同的,它們無(wú)法以一種合乎情理的方式共存。第一個(gè)例子字符數(shù)組filename的內(nèi)存布局如下圖所示:

第二種方式字符指針filename的內(nèi)存布局如下圖所示:

修改方法如下圖所示:
char filename[] = "/etc/passwd"; extern char filename[];
也可以這樣進(jìn)行修改:
char*filename = "/etc/passwd"; extern char *filename;
6 頭文件
注意:每個(gè)外部對(duì)象只在一個(gè)地方聲明,這個(gè)聲明的地方一般就在頭文件種,需要用到該外部對(duì)象的所有模塊也應(yīng)該 包括在這個(gè)頭文件。特別指出的是,定義該外部對(duì)象的模塊也應(yīng)該包括這個(gè)頭文件。
到此這篇關(guān)于C語(yǔ)言?超詳細(xì)講解鏈接器的文章就介紹到這了,更多相關(guān)C語(yǔ)言 鏈接器內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
結(jié)構(gòu)體類(lèi)型數(shù)據(jù)作為函數(shù)參數(shù)(三種方法)
將一個(gè)結(jié)構(gòu)體中變量中的數(shù)據(jù)傳遞給另一個(gè)函數(shù),有以下三種方法。需要的朋友可以過(guò)來(lái)參考下,希望對(duì)大家有所幫助2013-10-10
C++數(shù)據(jù)結(jié)構(gòu)之二叉搜索樹(shù)的實(shí)現(xiàn)詳解
二叉搜索樹(shù)作為一個(gè)經(jīng)典的數(shù)據(jù)結(jié)構(gòu),具有鏈表的快速插入與刪除的特點(diǎn),同時(shí)查詢(xún)效率也很優(yōu)秀,所以應(yīng)用十分廣泛。本文將詳細(xì)講講二叉搜索樹(shù)的C++實(shí)現(xiàn),需要的可以參考一下2022-08-08
C語(yǔ)言實(shí)現(xiàn)統(tǒng)計(jì)一行字符串的單詞個(gè)數(shù)
這篇文章主要介紹了C語(yǔ)言實(shí)現(xiàn)統(tǒng)計(jì)一行字符串的單詞個(gè)數(shù),具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-07-07
Java C++ 算法題解leetcode1582二進(jìn)制矩陣特殊位置
這篇文章主要為大家介紹了Java C++ 算法題解leetcode1582二進(jìn)制矩陣特殊位置示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-09-09

