編譯與連結的區別

  編譯和連結都是為將使用者程式從硬碟上調入記憶體並將其轉換為可執行程式服務的。用編譯器時的compile就是在進行編譯,link就是連結,它們兩者之間還有什麼區別呢?下面,小編帶你去看一看。

  在多道程式環境中,要想將一個使用者原始碼變成一個可以在記憶體中執行的程式,通常分為三個步驟:編譯、連結、載入。

  一、含義

  ***1***編譯:由編譯程式將使用者的原始碼編譯成若干個目標模組。

  ***2***連結:由連結程式將編譯後形成的一組目標模組以及它們所需要的庫函式連結在一起,形成一個完整的載入模組。

  ***3***載入:由載入程式將載入模組載入記憶體

  二、分類

  連結分三種:靜態連結、載入時動態連結、執行時動態連結,現在流行的是執行時動態連結,這種不僅可以回憶程式的載入過程,而且節省了大量的記憶體空間。

  三、編譯和連結的區別

  無論是C/C++,首先要把原始檔編譯成中間程式碼檔案,在Windows下面就是.obj檔案,Unix、Linux下面就是.o檔案,即Object File,這個動作叫編譯***compile***。然後再把大量的Object File合成執行檔案,這個動作叫作連結***link***。

  編譯時,編譯器需要的是語法的正確,函式與變數的宣告的正確。一般來說,每個原始檔都應該對應於一箇中間目標檔案***.o檔案或是.obj檔案***。

  連結時,主要是連結函式和全域性變數,所以,我們可以使用這些中間目標檔案***.o檔案或是.obj檔案***來連結我們的應用程式。連結就是那些目標檔案之間相互連結自己所需要的函式和全域性變數,而函式可能來源於其他目標檔案或庫檔案。

  原始檔首先會生成中間目標檔案,再由中間目標檔案生成執行檔案。在編譯時,編譯器只檢測程式語法,和函式、變數是否被宣告。如果函式未被宣告,編譯器會給出一個警告,但可以生成Object File。而在連結程式時,連結器會在所有的Object File中找尋函式的實現,如果找不到,那到就會報連結錯誤碼***Linker Error***,在VC下,這種錯誤一般是:Link 2001錯誤,意思說是說,連結器未能找到函式的實現,需要指定函式的Object File。

  小編提示

  編譯的時候採用 -Lxxx -lyyy 的形式使用庫,-L和-l這個引數並沒有配對的關係,我們的一些Makefile 為了維護方便把他們寫成配對的形式,造成了誤解.其實完全可以寫成 -Lpath1, -Lpath2, -Lpath3, -llib1 這樣的形式。

  在具體連結的時候,gcc是以.o檔案為單位, 編譯的時候如果寫 g++ -o main main.cpp libx.o 那麼無論main.cpp中是否使用到libx.o,libx.o中的所有符號都會被載入到mian函式中.但是如果是針對.a,寫成g++ -o main main.cpp -L./ -lx, 這個時候gcc在連結的時候只會連結有被用到.o, 如果出現libx.a中的某個.o檔案中沒有任何一個符號被main用到,那麼這個.o就不會被連結到main中重定位。

  經過上面的符號解析後,所有的符號都可以找到它所對應的實際位置***U表示的連結找到具體的符號位置***.

  as 彙編生成一個目標模組的時候,它不知道資料和程式碼在最後具體的位置,同時也不知道任何外部定義的符號的具體位置,所以as在生成目的碼的時候,對於位置未知的符號,它會生成一個重定位表目,告訴連結器在將目標檔案合併成可執行檔案時候如何修改地址成最終的位置。

  採用gcc 和g++ 在編譯的時候產生的符號有所不同。

  在C++中由於要支援函式過載,名稱空間等特性,g++會把函式+引數***可能還有名稱空間***,把函式命變成一個特殊並且唯一的符號名.例如:int foo***int a***;

  在gcc編譯後,在符號表中的名字就是函式名foo, 但是在g++編譯後名字可能就變成了_Z3fooi, 我們可以使用 c++filt命令把一個符號還原成它原本的樣子。