庫 是一種代碼的二進位的封裝形式,將.o文件打包封裝就成了庫。庫可以在任何地方使用,但用戶卻不能看見他的具體實現。庫有利於代碼模塊化,只要介面設計得合理,改變庫的內部實現,不會影響到用戶級別的代碼使用。 動態庫 1.封裝動態庫 假設有源代碼sum.c, sub.c gcc sum.c -c -o s ...
庫 是一種代碼的二進位的封裝形式,將.o文件打包封裝就成了庫。庫可以在任何地方使用,但用戶卻不能看見他的具體實現。庫有利於代碼模塊化,只要介面設計得合理,改變庫的內部實現,不會影響到用戶級別的代碼使用。
動態庫
1.封裝動態庫
假設有源代碼sum.c, sub.c
gcc sum.c -c -o sum.o
gcc sub.c -c -o sub.o
//封裝成庫
gcc -shared -fPIC -o libmymath.so sum.o sub.o
/*
-shared 表示要編譯一個共用庫
-fPIC 表示要生成與位置無關的代碼
-o 要創建的庫的名稱,一般約定庫的名稱格式如下:
lib庫名.so
*/
2.動態庫的編譯
只需把.h與.so文件提供給用戶使用即可
用戶編譯形式如下:
gcc main.c -I 頭文件路徑 -L 庫文件路徑 -l庫名
ex:
gcc test.c -I ../mymath/ -L ../mymath/ -lmymath
//路徑可以是相對路徑也可以是絕對路徑
3.依賴動態庫的程式執行
用戶執行時需要指明庫所在的路徑,通常通過添加環境變數的方式實現
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:庫文件路徑
查看庫環境變數
echo $LD_LIBRARY_PATH
使用命令lld可查看
靜態庫
1.封裝靜態庫
假設有源代碼sum.c, sub.c
gcc sum.c -c -o sum.o
gcc sub.c -c -o sub.o
//靜態庫的尾碼名是.a
ar -rc libmymath.a sum.o sub.o
//-rc 中的r 表示插入目標文件到靜態庫中,c表示創建指定的靜態庫
2.靜態庫編譯
gcc main.c -I 頭文件路徑 -L 庫文件路徑 -l庫名
ex:
gcc test.c -I ../mymath/ -L ../mymath/ -lmymath
//如果該路徑下有動態庫,則會優先使用動態庫
//如果要靜態鏈接自己或第三方的庫,但又要動態鏈接系統的庫,那麼編譯方式如下
gcc xxx.c -I 頭文件路徑 靜態庫的完整的名稱
ex:
gcc xxx.c -I ../mymath/ ../mymath/libmymath.a
//全部使用靜態庫,在編譯時加 -static
gcc main.c -I 頭文件路徑 -L 庫文件路徑 -l庫名 -static
動態庫與靜態庫的主要區別
靜態庫和動態庫都是二進位文件(目標文件)的封裝
對於動態庫,編譯程式時,並沒有把動態庫的內容複製到可執行文件中去,僅僅是做了一個標記,表示可執行文件需要用到某個動態庫,當執行程式時,首先需要去LD_LIBRARY_PATH指定的路徑下(或庫的標準路徑,如/lib,如/usr/lib等)查找需要的動態庫,才能正常運行。
對於靜態庫,編譯程式時,會把靜態庫中的內容複製到可執行文件中去,運行程式時,就不再需要那個靜態庫了。
我們使用得最多的是動態庫,理由:
1、程式運行時,動態庫在記憶體中只需要一份,而靜態庫,則可能會有多份拷貝,造成所謂的代碼
冗餘。
2、當庫更新升級時,對於動態庫來說只要介面不變,則不需要重新編譯用戶程式,如果是靜態庫,則庫改變了,所有使用該庫的程式都必須重新編譯。
C&C++混合編程中庫的處理
c++相容c,能夠直接使用這些功能,用c開發的功能如果打包成庫了(經c編譯器編譯成目標文件),目標文件中的函數名就已經確定下來。而c++編譯器會對函數名進行處理,會由於函數名字不匹配而導致調用c庫中的函數失敗
解決辦法:
C++提供了一個關鍵字: extern "C", 稱為鏈接指示,通常寫在頭文件中。但c編譯器並不認識這個關鍵字。因此用法如下:
#ifdef __cplusplus
extern "C"
{
#endif
int sum(int a, int b); // C函數聲明
int sub(int a, int b); // 可包含多個函數聲明
#ifdef __cplusplus
}
#endif
負責任的程式員,理應在c語言編寫的庫中加入該關鍵字