編譯與鏈接的區(qū)別
編譯和鏈接都是為將用戶程序從硬盤(pán)上調(diào)入內(nèi)存并將其轉(zhuǎn)換為可執(zhí)行程序服務(wù)的。用編譯器時(shí)的compile就是在進(jìn)行編譯,link就是鏈接,它們兩者之間還有什么區(qū)別呢?下面,學(xué)習(xí)啦小編帶你去看一看。
在多道程序環(huán)境中,要想將一個(gè)用戶源代碼變成一個(gè)可以在內(nèi)存中執(zhí)行的程序,通常分為三個(gè)步驟:編譯、鏈接、載入。
一、含義
(1)編譯:由編譯程序?qū)⒂脩舻脑创a編譯成若干個(gè)目標(biāo)模塊。
(2)鏈接:由鏈接程序?qū)⒕幾g后形成的一組目標(biāo)模塊以及它們所需要的庫(kù)函數(shù)鏈接在一起,形成一個(gè)完整的載入模塊。
(3)載入:由載入程序?qū)⑤d入模塊載入內(nèi)存
二、分類
鏈接分三種:靜態(tài)鏈接、載入時(shí)動(dòng)態(tài)鏈接、運(yùn)行時(shí)動(dòng)態(tài)鏈接,現(xiàn)在流行的是運(yùn)行時(shí)動(dòng)態(tài)鏈接,這種不僅可以回憶程序的載入過(guò)程,而且節(jié)省了大量的內(nèi)存空間。
三、編譯和鏈接的區(qū)別
無(wú)論是C/C++,首先要把源文件編譯成中間代碼文件,在Windows下面就是.obj文件,Unix、Linux下面就是.o文件,即Object File,這個(gè)動(dòng)作叫編譯(compile)。然后再把大量的Object File合成執(zhí)行文件,這個(gè)動(dòng)作叫作鏈接(link)。
編譯時(shí),編譯器需要的是語(yǔ)法的正確,函數(shù)與變量的聲明的正確。一般來(lái)說(shuō),每個(gè)源文件都應(yīng)該對(duì)應(yīng)于一個(gè)中間目標(biāo)文件(.o文件或是.obj文件)。
鏈接時(shí),主要是鏈接函數(shù)和全局變量,所以,我們可以使用這些中間目標(biāo)文件(.o文件或是.obj文件)來(lái)鏈接我們的應(yīng)用程序。鏈接就是那些目標(biāo)文件之間相互鏈接自己所需要的函數(shù)和全局變量,而函數(shù)可能來(lái)源于其他目標(biāo)文件或庫(kù)文件。
源文件首先會(huì)生成中間目標(biāo)文件,再由中間目標(biāo)文件生成執(zhí)行文件。在編譯時(shí),編譯器只檢測(cè)程序語(yǔ)法,和函數(shù)、變量是否被聲明。如果函數(shù)未被聲明,編譯器會(huì)給出一個(gè)警告,但可以生成Object File。而在鏈接程序時(shí),鏈接器會(huì)在所有的Object File中找尋函數(shù)的實(shí)現(xiàn),如果找不到,那到就會(huì)報(bào)鏈接錯(cuò)誤碼(Linker Error),在VC下,這種錯(cuò)誤一般是:Link 2001錯(cuò)誤,意思說(shuō)是說(shuō),鏈接器未能找到函數(shù)的實(shí)現(xiàn),需要指定函數(shù)的Object File。
小編提示
編譯的時(shí)候采用 -Lxxx -lyyy 的形式使用庫(kù),-L和-l這個(gè)參數(shù)并沒(méi)有配對(duì)的關(guān)系,我們的一些Makefile 為了維護(hù)方便把他們寫(xiě)成配對(duì)的形式,造成了誤解.其實(shí)完全可以寫(xiě)成 -Lpath1, -Lpath2, -Lpath3, -llib1 這樣的形式。
在具體鏈接的時(shí)候,gcc是以.o文件為單位, 編譯的時(shí)候如果寫(xiě) g++ -o main main.cpp libx.o 那么無(wú)論main.cpp中是否使用到libx.o,libx.o中的所有符號(hào)都會(huì)被載入到mian函數(shù)中.但是如果是針對(duì).a,寫(xiě)成g++ -o main main.cpp -L./ -lx, 這個(gè)時(shí)候gcc在鏈接的時(shí)候只會(huì)鏈接有被用到.o, 如果出現(xiàn)libx.a中的某個(gè).o文件中沒(méi)有任何一個(gè)符號(hào)被main用到,那么這個(gè).o就不會(huì)被鏈接到main中重定位。
經(jīng)過(guò)上面的符號(hào)解析后,所有的符號(hào)都可以找到它所對(duì)應(yīng)的實(shí)際位置(U表示的鏈接找到具體的符號(hào)位置).
as 匯編生成一個(gè)目標(biāo)模塊的時(shí)候,它不知道數(shù)據(jù)和代碼在最后具體的位置,同時(shí)也不知道任何外部定義的符號(hào)的具體位置,所以as在生成目標(biāo)代碼的時(shí)候,對(duì)于位置未知的符號(hào),它會(huì)生成一個(gè)重定位表目,告訴鏈接器在將目標(biāo)文件合并成可執(zhí)行文件時(shí)候如何修改地址成最終的位置。
采用gcc 和g++ 在編譯的時(shí)候產(chǎn)生的符號(hào)有所不同。
在C++中由于要支持函數(shù)重載,命名空間等特性,g++會(huì)把函數(shù)+參數(shù)(可能還有命名空間),把函數(shù)命變成一個(gè)特殊并且唯一的符號(hào)名.例如:int foo(int a);
在gcc編譯后,在符號(hào)表中的名字就是函數(shù)名foo, 但是在g++編譯后名字可能就變成了_Z3fooi, 我們可以使用 c++filt命令把一個(gè)符號(hào)還原成它原本的樣子。