GCC編譯的過程大約分爲三個過程:預處理,優化與編譯,鏈接;
首先看預處理階段:預處理階段要將源程序做一些處理,比如宏定義的替換等,最終生成的文件內容是彙編代碼,GCC默認不生成這種中間代碼的文件,但可以通過命令:
gcc -S example1.c -o example1.S
在這生成的example1.S就是一個彙編代碼文件,注意其後綴不是非加不可的,實際上LINUX對後綴並不敏感,而是通過系統自動識別文件類型,但加上後綴,無疑對我們自己而言更具可讀性。
優化與編譯:
優化過程可以分爲兩類,一是對程序本身的優化,二是對程序能更好的適應特定機器硬件上的優化
編譯就是對預處理過程生成的彙編代碼進一步處理生成電腦能識別的二進制目標文件。
使用命令:
gcc -c example1.c -o example1.o
如果加入優化選項:
gcc -O -c example1.c -o example1.o
這裏example1.o就是一個目標文件,目標文件仍然不是一個可以執行的文件,它要經過鏈接才能變成可執行文件
命令:
gcc example1.c -o example1
生成的example1就是一個可執行文件,這裏並不會產生example1.S 與example1.o的中間文件
下面對上述命令的格式做一個統一的說明:
gcc [選項] 源文件 [-o] 目標文件
在這裏,[選項]可以說控制的是gcc的執行操作,如果沒有[選項],默認是將源文件,執行到生成可執行文件爲止,就是預處理,編譯與優化,鏈接一氣呵成。
[-o]意味着是要對生成的文件命名,當然不是必須的,但既然生成的文件是自己需要與使用的文件,對其命上自己取的名字是完全有必要的。
下面來重點介紹:鏈接
鏈接分爲:靜態鏈接與動態鏈接
所謂靜態鏈接:將所需要的函數庫中的目標代碼靜態添加到可執行文件中
動態鏈接:將所需函數庫的函數名,路徑信息等添加到可執行文件中,執行過程中動態加載所需函數庫
例如:一個主程序使用了printf函數,在靜態鏈接下,它是將printf 函數代碼加入到目標代碼中,而動態連接中只是將printf在libc中的位置加入到目標代碼中,在執行過程中再動態加載進去。動態鏈接的好處是,當要加載的函數庫內部發生改變時,只需改變其函數庫即可,無需改動主程序,也不需重新編譯,而如果是靜態鏈接,就得重新編譯與外國鏈接。一般來說實行動態鏈接的目標文件相比實行靜態鏈接的目標文件來說要小些。
下面看兩個命令:
gcc -s example1.c -o example1 (指定動態鏈接)
gcc example1.c -o example1 (默認靜態鏈接)
生成(函數)庫文件:一般可大分爲兩種函數庫文件 靜態函數庫與動態(共享)函數庫
創建靜態函數庫過程: gcc -c example1.c -I(所需頭文件所在目錄) -o example1.o
gcc -c example2.c -I(所需頭文件所在目錄) -o example2.o
ar -rv libexample.a example1.o example2.o (創建靜態函數庫,將這兩個目標文件添加至函數庫中)
ar -tv libexample.a (查看函數庫中文件)
靜態鏈接函數庫: gcc example.c -I(所需頭文所在目錄) -L(所需函數庫所在目錄) -lexample(指定所需的函數庫名) -o example
動態鏈接函數庫: gcc -s example.c -I(所需頭文所在目錄) -L(所需函數庫所在目錄) -lexample(指定所需的函數庫名) -o example
創建動態函數庫過程: gcc -fPIC -c example1.c -I(所需頭文件所在目錄) -o example1.o
gcc - fPIC -c example2.c -I(所需頭文件所在目錄) -o example2.o
gcc -shared example1.o example2.o -o libexample.so (創建動態函數庫,將這兩個目標文件添加至函數庫中)
靜態鏈接函數庫: gcc example.c -I(所需頭文所在目錄) -L(所需函數庫所在目錄) -lexample(指定所需的函數庫名) -o example
動態鏈接函數庫: gcc -s example.c -I(所需頭文所在目錄) -L(所需函數庫所在目錄) -lexample(指定所需的函數庫名) -o example
注意要指定LD_LIBRARY_PATH至函數庫文件所在目錄,才能正常使用函數庫
echo $LD_LIBRARY_PATH (查看)
export LD_LIBRARY_PATH=./ (賦值)