- 作用
- 分離編譯
- 代碼重用
- 分類
| 分類 | 作用 | 后綴 |
|---|---|---|
| 靜態(tài)庫 | 一個或多個.o目標(biāo)文件歸檔在一個文件中 |
.a |
| 共享庫 | 沒有main函數(shù)的可執(zhí)行文件 |
.so |
| 動態(tài)加載庫 | 沒有main函數(shù)的可執(zhí)行文件,接口復(fù)合API |
.so |

0.前提(文件內(nèi)容)
test.cpp
#include <iostream>
#include "test.h"
using namespace std;
void Func(int i){
cout << __func__ << "(" << i << ")" << endl;
}
- test.h
#ifndef __TEST_H_
#define __TEST_H_
void Func(int i);
#endif
- main.cpp
#include "test.h"
int main(){
Func();
return 0;
}
1. 靜態(tài)庫的制作與使用
1.1 創(chuàng)建
- 編譯源文件
g++ -c -o test.o test.cpp
- 生成靜態(tài)庫
ar -rcs libtest.a test.o
-
ar選項(xiàng)
| 命令選項(xiàng) | 作用 |
|---|---|
r |
替換模塊(replace) |
c |
創(chuàng)建庫(create) |
s |
建立索引 |
tar和ar都是歸檔工具
tar用于創(chuàng)建.tar歸檔文件。ar用于創(chuàng)建歸檔文件,并且為歸檔的目標(biāo)文件中的符號建立索引。
- 查看目標(biāo)文件的符號(symbol)信息
nm 目標(biāo)文件
目標(biāo)文件可以是.o、.a,也可以是可執(zhí)行文件。
1.2 使用
- 鏈接靜態(tài)庫
g++ -o main main.cpp -L. -ltest
或者
g++ -o main main.cpp ./libtest.a
注意:庫一定要放在命令行的末尾
- 測試
./main
- 結(jié)果
Func(100)
2. 共享庫的制作
2.1 創(chuàng)建
- 編譯目標(biāo)文件
g++ -c -fPIC test.cpp -o test.o
- 生成動態(tài)庫
g++ -shared test.o -o libtest.so
以上兩步可以合并為
g++ -shared -fPIC -o libtest.so test.cpp
- 選項(xiàng)說明
| 命令選項(xiàng) | 作用 |
|---|---|
shared |
創(chuàng)建動態(tài)庫 |
fPIC |
代碼都是與位置無關(guān)的 |
每個共享函數(shù)庫都有個特殊的名字,稱作
soname。soname名字命名必須以lib作為前綴,然后是函數(shù)庫的名字,然后是.so,最后是版本號信息。不過有個特例,就是非常底層的C庫函數(shù)都不是以lib開頭這樣命名的。
2.2 使用
- 生成可執(zhí)行文件
g++ -o main main.cpp -L. -ltest
或者
g++ -o main main.cpp ./libtest.so
注意:庫一定要放在命令行的末尾
- 測試
指定動態(tài)鏈接庫位置
export LD_LIBRARY_PATH=動態(tài)鏈接庫位置
執(zhí)行
./main
- 結(jié)果
Func(100)
關(guān)于動態(tài)鏈接庫的安裝路徑
- 如果安裝在
/lib或者/usr/lib下,那么ld默認(rèn)能夠找到,無需其他操作。- 如果安裝在其他目錄,需要將其添加到
/etc/ld.so.cache文件中,步驟如下:
編輯/etc/ld.so.conf文件,加入庫文件所在目錄的路徑
運(yùn)行ldconfig,該命令會重建/etc/ld.so.cache文件
當(dāng)靜態(tài)庫和動態(tài)庫同名時, gcc命令將優(yōu)先使用動態(tài)庫。
- 查看執(zhí)行文件鏈接的動態(tài)鏈接庫
ldd 可執(zhí)行文件
也可以查看動態(tài)鏈接庫所鏈接的其它動態(tài)庫。
3. 動態(tài)加載庫
3.1 創(chuàng)建
- 修改
test.h
#ifndef __TEST_H_
#define __TEST_H_
#ifdef __cpluscplus
extern "C" //C++
{
#endif
void Func(int i);
#ifdef __cpluscplus
}
#endif
#endif // __TEST_H_
extern關(guān)鍵字:在一個項(xiàng)目中必須保證函數(shù)、變量、枚舉等在所有的源文件中保持一致,除非你指定定義為局部的。
extern "C"指令中的C,表示的一種編譯和連接規(guī)約,而不是一種語言。C表示符合C語言的編譯和連接規(guī)約的任何語言
extern "C"的真實(shí)目的是實(shí)現(xiàn)類C和C++的混合編程。在C++源文件中的語句前面加上extern "C",表明它按照類C的編譯和連接規(guī)約來編譯和連接,而不是C++的編譯的連接規(guī)約。__cplusplus的值是為了表示C++的版本,目前不應(yīng)該依賴該宏的值。
- 修改
main.cpp
#include <iostream>
#include <cstdlib>
#include <dlfcn.h>
using namespace std;
int main(){
void *so_handle = dlopen("./libtest.so", RTLD_LAZY); // 加載.so文件
if (!so_handle) {
cerr << "Error: load so failed" << endl;
exit(-1);
}
typedef void func_t(int);
func_t *pFunc = (func_t *)dlsym(so_handle,"Func");
char *err = dlerror();
if (NULL != err) {
cerr << "load Func err:" << err << endl;
exit(-1);
}
pFunc(100);
dlclose(so_handle); // 關(guān)閉so句柄
return 0;
}
- 重新編譯
.so
g++ -shared -fPIC test.cpp -o libtest.so
3.2 使用
- 重新編譯
main.cpp
g++ -o main main.cpp -ldl
- 執(zhí)行
.\main
- 結(jié)果
Func(100)
3.3 動態(tài)庫(共享庫、動態(tài)加載庫)與靜態(tài)庫的區(qū)別
- 靜態(tài)庫在程序編譯時會被連接到目標(biāo)代碼中,程序運(yùn)行時將不再需要該靜態(tài)庫,因此體積較大。
- 動態(tài)庫在程序編譯時并不會被連接到目標(biāo)代碼中,而是在程序運(yùn)行是才被載入,因此在程序運(yùn)行時還需要動態(tài)庫存在,因此代碼體積較小。


3.4 動態(tài)加載與靜態(tài)加載的區(qū)別
- 動態(tài)加載
- 靈活,可以在需要的時候進(jìn)行加載,在不需要的時候進(jìn)行卸載,這樣可以不必占用內(nèi)存。
- 可以在沒有動態(tài)庫時候發(fā)現(xiàn),而不致程序報(bào)錯。
- 加載程序中有條件才運(yùn)行的庫。
- 熱更新,在不停止程序的前提下進(jìn)行更新。
- 復(fù)雜一些,需要顯示獲得函數(shù)地址。
- 靜態(tài)加載
- 簡單方便
- 沒有找到動態(tài)庫時,系統(tǒng)報(bào)錯
- 加載運(yùn)行很久的庫
4. 總結(jié)

靜態(tài)庫、共享庫與動態(tài)庫編譯鏈接使用比較

靜態(tài)庫與動態(tài)庫Make file比較
5. 補(bǔ)充
使用動態(tài)庫的方法還有如下幾種
-
方法一:連接前,添加動態(tài)庫目錄到環(huán)境變量
LD_RUN_PATH。export LD_RUN_PATH=動態(tài)庫目錄 方法二:編譯鏈接時,添加鏈接選項(xiàng)
-Wl,-rpath -Wl,動態(tài)庫目錄-
方法三:執(zhí)行前,添加動態(tài)庫目錄到環(huán)境變量
LD_LIBRARY_PATH。export LD_LIBRARY_PATH=動態(tài)庫目錄 -
方法四:添加共享庫目錄
/usr/local/lib到共享庫配置文件echo 動態(tài)庫目錄 >> /etc/ld.so.conf ldconfig