了解c/c++編譯器的基本使用昧互,能夠在后續(xù)移植第三方框架進行交叉編譯時(編譯android可用的庫),清楚的了解應(yīng)該傳遞什么參數(shù)。android的Android.mk就是一段段Makefile單元,很多第三方庫直接提供makefile,需要能夠大致的讀懂makefile文件谅年。
gcc/g++
gcc
GNU C編譯器。原本只能處理C語言肮韧,很快擴展融蹂,變得可處理C++。
g++
gcc和g++都能夠編譯c/c++弄企,但是編譯時候行為不同超燃。
對于gcc與g++:
- 后綴為.c的源文件,gcc把它當作是C程序拘领,而g++當作是C++程序意乓;后綴為.cpp的,兩者都會認為是c++程序约素。
- g++會自動鏈接c++標準庫stl届良,gcc不會笆凌。
- gcc不會定義__cplusplus宏,而g++會士葫。
編譯器過程
一個C/C++文件要經(jīng)過預(yù)處理(preprocessing)乞而、編譯(compilation)、匯編(assembly)为障、和鏈接(linking)才能變成可執(zhí)行文件晦闰。
1、預(yù)處理
?gcc -E main.c -o main.i
?-E的作用是讓gcc在預(yù)處理結(jié)束后停止編譯鳍怨。
?預(yù)處理階段主要處理include和define等。它把#include包含進來的.h 文件插入到#include所在的位置跪妥,把源程序中使用到的用#define定義的宏用實際的字符串代替
2鞋喇、編譯階段
gcc -S main.i -o main.s
-S的作用是編譯后結(jié)束,編譯生成了匯編文件眉撵。
?在這個階段中侦香,gcc首先要檢查代碼的規(guī)范性、是否有語法錯誤等纽疟,以確定代碼的實際要做的工作罐韩,在檢查無誤后,gcc把代碼翻譯成匯編語言污朽。
3散吵、匯編階段
gcc -c main.s -o main.o
匯編階段把 .s文件翻譯成二進制機器指令文件.o,這個階段接收.c, .i, .s的文件都沒有問題。
4蟆肆、鏈接階段
?gcc -o main main.s
鏈接階段矾睦,鏈接的是函數(shù)庫。在main.c中并沒有定義”printf”的函數(shù)實現(xiàn)炎功,且在預(yù)編譯中包含進的”stdio.h”中也只有該函數(shù)的聲明枚冗。系統(tǒng)把這些函數(shù)實現(xiàn)都被做到名為libc.so的動態(tài)庫。
/etc/ld.so.conf
在沒有特別指定時蛇损,gcc會到系統(tǒng)編譯器只會使用/lib和/usr/lib這兩個目錄下的庫文件赁温。如果存在一個so不在這兩個目錄,在編譯時候就會出現(xiàn)找不到的情況淤齐。
/etc/ld.so.conf文件中可以指定而外的編譯鏈接庫路徑股囊。
輸入:cat /etc/ld.so.conf:
include /etc/ld.so.conf.d/*.conf #引入其他的conf文件
/usr/local/lib #增加庫搜索目錄
#編輯完成后 使用 ldconfig 更新
靜態(tài)庫和動態(tài)庫
庫通常以前綴“ lib”命名。對于所有C標準庫都是如此床玻。鏈接時毁涉,對庫的命令行引用將不包含庫前綴或后綴。
比如liblog.so這個庫锈死,引用的使用去掉前綴lib和后綴.so即可引用該庫贫堰。
.a 為靜態(tài)庫穆壕,可以是一個或多個.o合在一起,用于靜態(tài)連接;多個.o文件可以鏈接生成一個.exe的可執(zhí)行文件其屏。靜態(tài)庫在程序編譯時會被鏈接到目標代碼中喇勋,相當于將你使用庫里的函數(shù)加載到程序里,在編譯的時候直接編譯進去偎行,這樣川背,在編譯之后執(zhí)行程序時將不再需要該靜態(tài)庫。編譯之后
程序文件大蛤袒,但加載快熄云,隔離性也好
。所以它的優(yōu)點就顯而易見了妙真,即編譯后的執(zhí)行程序不需要外部的函數(shù)庫支持缴允,因為所有使用的函數(shù)都已經(jīng)被編譯進去了。當然這也會成為它的缺點珍德,因為如果靜態(tài)函數(shù)庫改變了练般,那么你的程序必須重新編譯。
.so 為動態(tài)庫(共享庫)锈候,類似windows平臺的.dll文件薄料。動態(tài)庫在程序
編譯時并不會被鏈接到目標代碼中,而是在編譯時僅引用泵琳,體積小
摄职,在程序運行到相關(guān)函數(shù)時才調(diào)用函數(shù)庫里的相應(yīng)函數(shù),才被載入虑稼,因此在程序運行時還需要動態(tài)庫存在
琳钉。多個應(yīng)用程序可以使用同一個動態(tài)庫,啟動多個應(yīng)用程序的時候蛛倦,只需要將動態(tài)庫加載到內(nèi)存一次即可歌懒。
簡而言之:
靜態(tài)庫節(jié)省時間,靜態(tài)庫在程序編譯時會被連接到目標代碼中溯壶,程序運行時將不再需要該靜態(tài)庫及皂。
動態(tài)庫節(jié)省空間,動態(tài)庫在程序編譯時并不會被連接到目標代碼中且改,而是在程序運行是才被載入验烧,因此在程序運行時還需要動態(tài)庫存在。
Java中在不經(jīng)過封裝的情況下只能直接使用動態(tài)庫又跛。
生成靜態(tài)庫
gcc -fPIC -c Test.c -o Test.o
ar r libTest.a Test.o
-fPIC 產(chǎn)生與位置無關(guān)代碼
可能會被不同的進程加載到不同的位置上碍拆,如果共享對象中的指令使用了絕對地址。
那么在共享對象被加載時就必須根據(jù)相關(guān)模塊的加載位置對這個地址做調(diào)整,也就是修改這些地址感混,
讓它在對應(yīng)進程中能正確訪問端幼,那么就不能實現(xiàn)多進程共享一份物理內(nèi)存(無法動態(tài)共享)
這里有一個例子,詳細介紹了靜態(tài)庫的生成和如何鏈接靜態(tài)庫弧满。
生成動態(tài)庫
gcc -fPIC -shared Test.c -o libTest.so
#或者
gcc -fPIC -c Test.c #生成.o
gcc -shared Test.o -o libTest.so
-fPIC:編譯器指令婆跑,用于輸出位置無關(guān)的代碼,這是共享庫所需的特征庭呜。
-shared:生成一個共享對象滑进,然后可以將其與其他對象鏈接以形成可執(zhí)行文件。
-Wl 選項:將選項傳遞給鏈接器募谎。
-o:操作輸出扶关。
這里有一個例子,詳細介紹了靜態(tài)庫的生成和如何鏈接靜態(tài)庫近哟。
使用庫
默認優(yōu)先使用動態(tài)庫
gcc main.c -L. -lTest -o main
強制使用靜態(tài)庫
-Wl 表示傳遞給 ld 鏈接器的參數(shù)
最后的 -Bdynamic 表示 默認仍然使用動態(tài)庫
gcc main.c -L. -Wl,-Bstatic -lTest -Wl,-Bdynamic -o main
使用動態(tài)庫鏈接的程序驮审,linux運行需要將動態(tài)庫路徑加入/etc/ld.so.conf。
查看可執(zhí)行文件符號
nm main
打包 .a 到so
--whole-archive: 將未使用的靜態(tài)庫符號(函數(shù)實現(xiàn))也鏈接進動態(tài)庫
--no-whole-archive : 默認吉执,未使用不鏈接進入動態(tài)庫
gcc -shared -o libMain.so -Wl,--whole-archive libMain.a -Wl,--no-whole-archive
頭文件與庫文件的指定
--sysroot=XX
使用xx作為這一次編譯的頭文件與庫文件的查找目錄,查找下面的 usr/include usr/lib目錄地来。
-isysroot XX
頭文件查找目錄,覆蓋--sysroot 戳玫,查找 XX/usr/include
-isystem XX
指定頭文件查找路徑(直接查找根目錄)
-IXX
頭文件查找目錄
優(yōu)先級:-I > -isystem > sysroot
-L:XX
指定庫文件查找目錄
-lxx.so
指定需要鏈接的庫名