目錄
前言:
使用 gcc/g++:
代碼的編譯過程:?
預處理:
頭文件展開:
宏替換+去注釋:?
?編輯
條件編譯:?
編譯:
匯編:
鏈接:
動態庫(動態鏈接):
靜態庫(靜態鏈接):
前言:
我們寫完一段源代碼后,需要讓代碼跑起來,在Linux中,我們使用 gcc/g++來編譯C語言/C++代碼。
使用 gcc/g++:
我們現在新建了一個文件,且代碼如下:
?gcc? [ 源代碼文件名 ] 就可以對指定的代碼文件進行編譯,編譯時會生成一個可執行程序,如果沒有為可執行程序命名,默認文件名為 a.out,運行 a.out ,就會運行我們所寫的代碼!
我們也可以為可執行程序命名,gcc? [ 源代碼文件名 ] ?-o? ?[ 可執行程序文件名 ]!
-o 后接目標文件。
g++ 的使用方法和 gcc 相同,這里不再贅述。
需要區別的是,gcc只能編譯 C語言 ,g++可以編譯 C語言 和 C++!
代碼的編譯過程:?
預處理:
預處理時,會對代碼進行宏替換、去掉注釋、條件編譯、頭文件展開,如果想查看預處理后的代碼,以 gcc 為例:gcc? -E? test.c? -o? test.i ,就可以把預處理后的代碼存放到指定的文件中,注意,目標文件后綴為 .i 。
-E 表示從現在開始進行程序的預處理過程,當預處理工作結束后,就停下來,不再進行后續的工作!?
頭文件展開:
雖然我們原來的代碼只有15行,經過頭文件展開后,代碼行數已經達到851行!
宏替換+去注釋:?
如果代碼中有注釋和宏定義,預處理時,會進行宏替換和刪除注釋,通過下面的對比可以看出:?
條件編譯:?
由于宏定義時只定義了VERSION1,在條件編譯時,只有VERSION1的代碼保留下來,其余未定義的全部被動態裁剪了!若只定義了VERSION2,或者沒有宏定義,也同理!
可以看出,預處理時,可以對代碼進行編輯和裁剪。
我們也可以在預處理的指令中進行宏定義, 即使在源代碼中我們沒有定義 VERSION1,但我們在指令中定義了,效果和在源代碼中定義相同!
指令: gcc -E? test.c? -o? test.i? -D? VERSION1=1
條件編譯可以防止頭文件重復包含!頭文件重復包含,會導致頭文件展開時,代碼量太大,導致編譯效率低下!我們需要規避頭文件重復包含!這是個好的編程習慣。??
#ifndef _NAME_H
#define _NAME_H
//頭文件內容
#endif
這里的 _NAME_H 可以是自定義的宏,這里的宏應為獨一無二的,不可以與文件中的其他宏的名字相同!
# ifndef _NAME_H (if not define _NAME_H ):
1、如果宏_NAME_H未定義,那么就會走 #define?_NAME_H 語句,定義宏 _NAME_H,并且引入頭文件;
2、如果宏_NAME_H已經定義了,則不會走 #define?_NAME_H 語句,直接進入#endif,不引入頭文件 。
編譯:
編譯時,把代碼轉為匯編語言。
-S:從現在開始進行程序的編譯,當編譯工作完成,就停下來!
形成匯編文件,可以直接在預處理的文件上進行編譯,也可以直接在源代碼上進行編譯,即兩種寫法:gcc? -S? test.c? -o? test.s? (在源代碼上)或?gcc? -S? test.i? -o? test.s(在預處理后的文件上)。
匯編:
匯編時,把編譯后生成的匯編語言轉為二進制語言!
-c :從現在開始進行程序的匯編,匯編工作結束后,就停下來!
同樣可以在源代碼的基礎上進行匯編,也可以在編譯文件上進行匯編!?
?轉為二進制后,代碼還不可以運行起來,即使開放了可執行權限,也是運行不起來的!
還需要最后一步--鏈接!?
鏈接:
直接 gcc test.o? -o? test 就可以進行鏈接,所謂鏈接,就是把代碼和庫文件進行鏈接,默認與動態庫進行鏈接。
?ldd 可以查看代碼和動態庫還是靜態庫產生鏈接:
在 Linux 中,動態庫以 .so 結尾,靜態庫以 .a 結尾;
在Windows中,動態庫以 .dll 結尾,靜態庫以 .lib 結尾。?
動態庫(動態鏈接):
動態庫在程序編譯時并不會被鏈接到目標代碼中,而是在程序運行時才被載入。
程序運行時,如果我們需要調用 printf 函數, printf 函數會被修改為庫函數中 printf 實現的地址,這時候動態鏈接就建立好了。
動態庫也叫共享庫。
優點:比較節省資源(內存資源、網絡資源等),不會出現太多的重復的代碼;
缺點:對庫的依賴性比較強,一旦庫丟失,所有依賴該庫的程序都無法運行。
?用 file 命令查看文件類型時,可以看出代碼是動態鏈接的:
靜態庫(靜態鏈接):
編譯鏈接時,把庫文件的代碼全部加入到可執行文件中。
優點:可執行程序不依賴庫,同類型平臺中進行跨平臺使用;
缺點:生成的文件比較大,比較浪費資源(占用內存--內存資源、網絡傳輸時傳輸量大--網絡資源等)。
代碼默認與動態庫鏈接,我們也可以讓代碼與靜態庫鏈接:
gcc -o mybin-static? test.c? -static 就可以讓代碼與靜態庫鏈接,但是系統是默認沒有安裝靜態庫的,此時靜態鏈接會出錯,我們需要在系統中下載靜態庫!
在普通用戶下,sudo? yum? install? -y? glibc-static? libstdc++-static 就可以安裝靜態庫。?
?安裝完成后,用 ldd 和 file 命令可以看出,test-static 與靜態庫鏈接。
test 是動態鏈接的,test-static 是靜態鏈接的,通過對比可以看出, 同樣的源代碼,靜態鏈接的代碼占用的空間比動態鏈接的大很多。
?
本文到這里就結束了,歡迎各位大佬的指正!?