讀完《程序員的自我修養(yǎng)--鏈接、裝載和庫(kù)》相關(guān)章節(jié),想來(lái)總結(jié)一下,若有錯(cuò)誤,請(qǐng)指正,多謝。
你的工程里有很多xxx.c這樣的源文件,這些文件是文本文件,只有人能夠認(rèn)識(shí)(當(dāng)然編譯器認(rèn)識(shí)),但是,cpu可不認(rèn)識(shí)。問(wèn)題就是,真正執(zhí)行指令的是cpu。
讓編譯器翻譯一下(這里面有很多過(guò)程,這不是這篇文章的重點(diǎn)),一般來(lái)說(shuō),一個(gè)xxx.c文件就能翻譯成一個(gè)xxx.o,這就是目標(biāo)文件了。
一個(gè)源文件就對(duì)應(yīng)一個(gè)目標(biāo)文件,這個(gè)目標(biāo)文件就存儲(chǔ)了有關(guān)這個(gè)源文件的所有信息了,包括在這個(gè)源文件里函數(shù)的定義,全局變量的定義,等等。
但是,這樣就可以毫無(wú)憂慮地執(zhí)行這個(gè)目標(biāo)文件了么? 不可以。
一, 你這個(gè)目標(biāo)文件可能沒(méi)有main函數(shù);
二, 你這個(gè)目標(biāo)文件里,可能用到了其他函數(shù),而這些函數(shù)的定義是在其他目標(biāo)文件里的。比如說(shuō),main.c 用到了 one.c 里的 void function(); 你去執(zhí)行main.c 生成的main.o,肯定不行啊,因?yàn)閏pu都找不到function在哪,從而function里存儲(chǔ)的指令,當(dāng)然也沒(méi)法執(zhí)行;
總之,你要運(yùn)行的那個(gè)文件,里面必須得存有一切函數(shù)和變量的相關(guān)信息才可以。很顯然,目標(biāo)文件不具有這個(gè)特性。因?yàn)椋繕?biāo)文件只存儲(chǔ)了自己的信息,并不知道其他目標(biāo)文件的信息。
好的,你有一個(gè)main.c 和 一個(gè) one.c, 并且成功的生成了兩個(gè)目標(biāo)文件,各自存儲(chǔ)了自身的信息,它們就是 main.o 和 one.o。
不巧的是,main.c 里用到了 one.c 里的 void function(); 函數(shù)。這個(gè)時(shí)候,main.o 苦于找不到這個(gè)函數(shù)在哪而不得執(zhí)行。而 one.o 靜靜的等在那,等待一個(gè)過(guò)程。
這個(gè)過(guò)程就是鏈接。
ld 是一個(gè)指令,linux 下,可以讓目標(biāo)文件鏈接起來(lái),拼成一個(gè)真正能用的可執(zhí)行文件。
例如這樣:
ld main.o one.o -o go
其中 -o 后面是隨意指定的,這就是可執(zhí)行文件的名稱。好了,這個(gè) go 就是最終的可執(zhí)行文件。你可以去執(zhí)行它了。
go 是由兩個(gè)目標(biāo)文件拼起來(lái)的,它當(dāng)然知道所有的信息,包括 具體的 function 的指令。于是,它就可以被執(zhí)行。
現(xiàn)在,到了這里,我們似乎忘了另一種重要的文件,頭文件。
好吧,問(wèn)題能提升你看這篇文章的樂(lè)趣。那就思考一個(gè)問(wèn)題:main.c 能成功編譯成 main.o么?
剛才的過(guò)程似乎太順利,main.c 刷一下就成了 main.o, 而問(wèn)題是,你在 main.c 里使用了一個(gè)它不認(rèn)得的函數(shù) void function(); 這個(gè)竟然能編譯過(guò),順利生成 main.o?
你可以試試,用這樣的命令:
gcc -c main.c -o main.o
-c 選項(xiàng)就是說(shuō),我要生成目標(biāo)文件,而不是默認(rèn)的可執(zhí)行文件。你一定會(huì)得到一個(gè)【編譯】錯(cuò)誤,這個(gè)錯(cuò)誤會(huì)告訴你,function 這個(gè)函數(shù)我不認(rèn)識(shí),敗!
【編譯】錯(cuò)誤,在源頭上先防止你造出一個(gè)完全不能用的程序。
這個(gè)時(shí)候,怎么辦,main.c 確實(shí)不認(rèn)得 function 函數(shù),你總不能把 one.c 里的函數(shù)復(fù)制粘貼到 main.c 里吧(當(dāng)然這是可以的,不過(guò),low爆了)。
那么需求如下:
一, 不拷貝過(guò)來(lái)整個(gè)函數(shù);
二, 讓 main.c 順利生成 main.o。
問(wèn)題的核心就是,讓 main.c 認(rèn)識(shí) function 是個(gè)啥(是函數(shù)還是變量?如果是函數(shù),這個(gè)函數(shù)的參數(shù)有哪些?返回什么類型的值?)。
容易,你在 main.c 源文件里加一句
extern void function(); // 這個(gè)函數(shù)的返回類型是 void, 并且沒(méi)有參數(shù)。
這樣一來(lái),main.c 本身就認(rèn)識(shí)了 function, 注意,只是認(rèn)識(shí),但是并不知道它具體實(shí)現(xiàn),也不知道這個(gè)函數(shù)在哪里。實(shí)際上,也不需要知道這么多。因?yàn)椋疫@一步只是生成目標(biāo)文件而已。剩下的交給鏈接那一步。
結(jié)論,生成目標(biāo)文件,必須得讓源文件認(rèn)識(shí)每一個(gè)符號(hào)(變量和函數(shù))。
假如 one.c 是你的同事編寫(xiě)的,你應(yīng)該讓他同時(shí)編寫(xiě)一個(gè)頭文件。省的你還要在你的 main.c 里 一行行地加上
extern void function1();
extern int function2(char a);
...
...
這種東西。
你的同事會(huì)給你一個(gè)頭文件 one.h , 這個(gè)頭文件里實(shí)際上就是以上extern的內(nèi)容。你只需要在 main.c 里這樣干:
#include "one.h"
就行了,這一句就是把 one.h 整個(gè)拷貝到 main.c 里去。
printf 用起來(lái)挺爽的。
你只用寫(xiě)上
#include <stdio.h>
讓你的 main.c 認(rèn)識(shí)這個(gè)函數(shù)就能用了。
不過(guò),問(wèn)題是,你并沒(méi)有鏈接 printf 所在的目標(biāo)文件啊!
思考,思考,再思考!
好吧,這不是個(gè)問(wèn)題。
實(shí)際就是,gcc 默認(rèn)幫你鏈接了。千萬(wàn)不要認(rèn)為,不用鏈接就可以!!!!
你可以這么認(rèn)為:只要是系統(tǒng)提供的東西,你都不用手動(dòng)鏈接,你關(guān)心好自己的東西就行了。
|
新聞熱點(diǎn)
疑難解答