1. 程式人生 > >編譯過程:預編譯、編譯、彙編與連結

編譯過程:預編譯、編譯、彙編與連結

原文地址:http://blog.csdn.net/u014120684/article/details/46352167

預編譯的副檔名是ii 

gcc -E hello.c -o hello.i 
預編譯過程主要處理原始碼檔案當中的以#開頭的預編譯指令,比如#include就是把標頭檔案插入到這個位置 #define就是把所有的巨集定義展開,還有就是刪除所有的註釋

編譯就是把i檔案編譯成為彙編程式碼檔案,彙編程式碼副檔名是.s 
gcc -S hello.i -o hello.s

但是現在版本的gcc把預編譯和編譯兩個步驟合併成為一個步驟, 
gcc -S hello.c -o hello.s 
或者編譯器ccl:ccl hello.c 
c++的編譯器:cclplus hello.cpp

彙編就是將組合語言的程式碼檔案轉變成機器語言,只是對照組合語言的指令和機器語言一條一條翻譯過來。 
使用匯編器as 
as hello.s -o hello.o 
或者 gcc -c hello.s -o hello.o 
或者最常用的預編譯,編譯,彙編三合一:gcc -c hello.c -o hello.o 或:gcc -c hello.c 
這麼看來,最後生成的目標檔案.o裡面就是機器語言,其實它本身就是按照可執行檔案格式儲存的,只是和真正的可執行檔案在結構上稍有不同。

連結:在一個程式被分為多個模組的時候,這些模組之間最後如何組合形成一個單一的程式是要解決的問題。模組之間如何組合的問題可以歸結為模組之間如何通訊的問題,最常見的屬於靜態語言的C/C++模組之間通訊有兩種方式,一種是模組之間的函式呼叫(可以看我的筆記:C++標頭檔案與CPP的關係),另外一種是模組間的變數訪問。 
函式訪問需要知道目標函式的地址,變數訪問也需要知道目標變數的地址。從原理上來講,連結的過程工作無非就是把函式和變數的引用加以修正,讓程式能找到函式和變數的地址。 
原始碼檔案.c編譯成為目標檔案.o,目標檔案和庫一起連結形成最終可執行檔案。最常見的庫就是執行時庫(runtime library). 
在連結的時候,假設我們有個全域性變數叫做var,他在目標檔案A裡面,我們在目標檔案B裡面要訪問這個全域性變數,比如在B裡面: 
mov1 $0x2a,var 
相當與C語言裡面的var=42 
在編譯目標檔案B的時候,編譯器並不知道var的目標地址,所以u編譯器在沒法確定地址的情況下,將這條Mov指令的目標地址置為0,等待連結器在將目標檔案A和B連結起來的時候再將其修正。

動態連結:解決空間浪費和更新困難的問題,不使用靜態庫而使用動態庫,動態連結就是把程式按照模組拆分成各個相對獨立的部分,在程式執行時才將他們連結在一起形成一個完整的程式,而不是像靜態連結那樣把所有的程式模組都連結成一個個單獨的可執行檔案。 
gcc -fPIC -shared 
-shared表示產生一個共享物件

比如一個靜態庫libtest.a和一個動態庫libtest.so,在連結libtest.a和hello.o的時候,生成的hello可執行檔案裡面就包含了libtest.a。以後執行hello的時候就算刪除了libtest.a也能執行出來。而在連結libtest.so和hello.o的時候,生成的hello可執行檔案裡面就沒libtest.so,如果刪除了libtest.so則不能執行。