1. 程式人生 > >make,makefile和程式的編譯連結過程

make,makefile和程式的編譯連結過程

一,Linux下程式執行過程
1,在一個目錄下新建三個檔案:main.c hello.c hello.h分別編寫他們如下圖:
小程式學習編譯連結執行過程
2,想要讓這個程式執行起來,就必須對上面的三個檔案分別進行編譯連結執行,如下圖:
目標檔案的編譯,生成.o檔案
連結生成可執行檔案mian,並執行main
通過上面這個過程。我們可以大致總結一下gcc編譯器把目標檔案經過預處理,編譯,彙編,連結生成可執行檔案的過程和命令:
(1)預處理(巨集替換,刪除註釋和多餘的空白字元,條件編譯,檔案包含):
預處理過程
其中選項-E進行檢視,這個選項的作用是讓gcc在預處理結束後停止編譯
過程。
選項-o是指目標檔案,.i檔案為已將完成預處理過程的C原始程式。
(2)編譯(gcc檢查程式碼規範性,是否有語法錯誤,生成彙編):


編譯
其中選項-S進行檢視,這個選項只進行編譯而不進行彙編,生成.s檔案。
(3)彙編(生成機器可識別程式碼,將編譯生成的.s檔案轉成.o二進位制目的碼):
彙編
(4)連結(生成可執行檔案或庫檔案):
在成功編譯後,就進入了連結的階段,這裡涉及到一個重要的概念:函式庫檢視上面的小程式會發現裡面並沒有定義printf函式的實現,並且在預編譯階段包含進去的“stdio.h”裡面也只有它的宣告,而沒有定義函式的實現。那麼printf實在哪裡實現的呢?
其實系統把對這些函式的實現都做到名字為libc.so.6的庫檔案中去了,在沒有特別指定時,gcc會到系統預設的路徑“/user/lib”
下面進行查詢,並且連結到lib.so.6庫函式中去,這樣就能實現printf函數了,這也就是連結的作用。
函式庫分為兩種:靜態庫和動態庫。靜態庫是指在編譯連結時將庫檔案的程式碼全部加入到可執行檔案裡,因此生成的檔案比較大,但在執行過程中就不會再用到庫檔案了,字尾名一般為.a;動態庫和它相反,在編譯連結過程中並沒有將庫檔案加入到可執行檔案裡面去,而是在程式執行時由執行時連結檔案載入庫,這樣可以節省系統的開銷,字尾名一般為.so。上面提到的libc.so.6就是動態庫,所以也就可以得到gcc在編譯時預設使用動態庫。完成連結過程,gcc就可以生成可執行檔案main了。
連結
(5)執行:
”./可執行檔名“就可以執行這個程式啦,輸出結果如下圖:
執行

二,make和makefile
1,對make和makefile的理解
像上面提到的那樣,要把一個程式執行起來首先要經過編譯生成中間檔案(.o檔案)(在編譯時編譯器只檢查程式語法,函式,變數是否被宣告,如果函式沒有被宣告,編譯器會給出警告,但還是會生成.o檔案。在連結時連結器會在所有的.o檔案裡尋找函式的實現,如果沒有找到,那就會報連結錯誤碼,就類似與這種:Link2001錯誤)再經過連結生成可執行檔案,這樣在小程式裡看起來沒什麼,但在大型的工程裡這卻是非常艱難煩躁的過程,你必須記住所有中間檔案的檔名,才能方便在連結時用到它們,但如果程式裡出現了一點小bug,那麼上面的過程都要重新來過。這樣效率實在太低了,所以就出現了makefile和make。
一個工程中的原始檔不計其數,其按型別,功能,模組分別放在若干個目錄中,makefile定義了一系列的規則來指定,哪些檔案要先編譯,哪些後編譯,哪些需要重新編譯,甚至進行更復雜的功能操作。makefile就像一個shell指令碼一樣,其中也可以執行作業系統的命令。makefile帶來的好處就是“自動化編譯”,一旦寫好,只需要一個make命令,整個工程完全自動編譯,極大的提高了軟體開發的效率。
make和makefile的區別:
make:是一個命令工具。
makefile:是一個存放編譯方法的檔案。
2,書寫makefile的格式和規則
make命令執行時,需要一個Makefile檔案,告訴make需要怎樣去編譯和連結程式。下面是makefile的書寫規則:
(1)如果這個工程沒有被編譯過,那麼我們的所有C檔案都要編譯並被連結。
(2)如果這個工程的某幾個C檔案被修改,那麼我們只編譯被修改的C檔案,並連結目標程式。
(3)如果這個工程的標頭檔案被改變了,那麼我們需要編譯引用了這幾個標頭檔案的C檔案,並連結目標程式。
Makefile檔案(形式1)
Makefile檔案(形式2)
注意:
在定義好依賴關係後,後續的那一行定義瞭如何生成目標檔案的作業系統命令,一定要以一個Tab鍵作為開頭。記住,make並不管命令是怎麼工作的,他只管執行所定義的命令。
clean不是檔案,是一個動作名詞,make clean用它來清除所有的目標檔案,以便於進行重編譯。
clean的兩種寫法
加@和不加@的對比
3,make是怎麼工作的?
(1)make會在當前目錄下找名字叫“Makefile”或“makefile”的檔案。
(2)如果找到,它會找檔案中的第一個目標檔案,在上面的例子中,他會找到“hello”這個檔案,並把這個檔案作為最終的目標檔案。
(3)如果hello檔案不存在,或是hello所依賴的後面的 .o 檔案的檔案修改時間要比hello這個檔案新,那麼,他就會執行後面所定義的命令來生
成hello這個檔案,這個也就是重編譯。
(4)如果hello所依賴的.o檔案也存在,那麼make會在當前檔案中找目標為.o檔案的依賴性,如果找到則再根據那一個規則生成.o檔案。(這
有點像一個堆疊的過程)
(5)當然,你的.c檔案和.h檔案是存在的啦,於是make會生成 .o 檔案,
然後再用 .o 檔案宣告make的終極任務,也就是執行檔案hello了。
make會一層一層去找檔案的依賴關係,直到最終編譯出第一個目標檔案,如果過程中出現了錯誤(被依賴的檔案找不到等),make會直接退出並報錯。而對於所定義的命令的錯誤,或是編譯不成功,make根本不理。make只管檔案的依賴性,即如果在我找了依賴關係之後,冒號後面的檔案還是不在,那麼對不起,make就不工作啦。
好啦,就說這麼多啦,有關make和makefile的其他內容後面會繼續完善的,有很多不足的地方希望小夥伴們幫忙指正。