1. 什么是庫
1.1 庫:
- 本質上來說庫是一種可執行的二進制代碼 (但不可以獨立執行), 可以被作業系統載入記憶體執行. 由于windows 和linux 的平臺不同 (主要是編譯器、匯編器和連接器的不同), 因此二者庫的二進制是不兼容的. 本文僅限于介紹linux 下的庫. linux 下的庫有兩種: 靜態庫和共享庫 (動態庫).
- Windows下的庫有兩種: 靜態庫(.lib)和元件(.dll).
- Linux下的庫有兩種: 靜態庫(.a)和共享庫(.so).
1.2 鏈接方式:
-
庫的鏈接方式分為靜態鏈接和動態鏈接
靜態鏈接: 鏈接靜態庫生成可執行程式, 直接將庫中我們用到的函式的實作代碼指令, 寫入到了可執行程式檔案中, 程式運行的時候沒有什么依賴.
動態鏈接: 鏈接動態庫生成可執行程式, 并沒有把庫中函式的實作指令直接拿過來寫入可執行程式中, 而是在可執行程式中記錄了庫中函式的符號資訊表, 在運行可執行程式的時候再去加載動態庫到記憶體中, 如果動態庫不存在, 則程式無法運行.

1.3 制作庫的目錄及源檔案:
-
在以下檔案夾中制作并存放動態庫與動態庫:
檔案夾main: 存放制作庫的源檔案, 以及測驗源檔案.
檔案夾share_libraries: ./lib 檔案夾中存放動態庫, ./include檔案夾中存放頭檔案.
檔案夾static_libraries: ./lib 檔案夾中存放動態庫, ./include檔案夾中存放頭檔案.

-
制作靜態庫與動態庫的原始碼及頭檔案如下:
add.cint add(int a, int b) { return a+b; }sub.c
int sub(int a, int b) { return a-b; }head.h
#ifndef __HEAD_H__ #define __HEAD_H__ int add(int a, int b); int sub(int a, int b); #endifmain.c
#include <stdio.h> #include "head.h" int main(int argc, char *argv[]) { printf("a+b=%d\n", add(3, 1)); printf("a-b=%d\n", sub(3, 1)); return 0; }
2. 靜態庫
2.1 什么是靜態庫
- 靜態庫檔案名的命名方式是"libxxxx.a", 庫名前加"lib", Windows和Linux下都是后綴".a", "xxx"為靜態庫名字, Windows下的靜態庫名也叫libxxx.a
- 鏈接時間: 靜態庫的代碼時在編譯程序中被載入程式中.
- 鏈接方式: 靜態庫的鏈接時將整個函式庫的所有資料都整合進了目標代碼. 這樣的優點時在編譯后的執行程式不在需要外部的函式庫支持, 因為所使用的函式都已經被編進去了. 缺點是, 如果所使用的靜態庫發生更新改變, 你的程式必須重新編譯.
2.2 靜態庫的制作
- 如果希望把原始碼file1.c, file2.c …fileN.c 做成庫檔案, 我們可以通過下命令把他們制作成靜態庫
gcc -c file1.c
gcc -c file2.c
…
gcc -c flieN.c
ar -rcs libname.a file1.o file2.o, … fileN.o
生成的libastatic.a為靜態庫, 將他拷貝到’/home/gyp/mylib/static_libraries/lib’ 路徑下. 將’mylib/main’ 路徑下的靜態庫洗掉.

2.3 靜態庫的使用
- 在進行編譯之前介紹一下與庫相關的 gcc 編譯選項:
-I(大寫i) 指定include包含檔案的搜索路徑.
-L 指定鏈接所需庫所在路徑
-l(小寫L) 指定所需鏈接庫的庫名(比如鏈接libastatic.a) -lastatic
-static: 靜態鏈接 - 編譯與運行:
$ gcc main.c -I …/static_libraries/include/ -L …/static_libraries/lib/ -lastatic -static
注意: -static 禁止使用動態庫, 讓鏈接靜態庫后的程式徹底的獨立起來, “完全靜態”, 因此, 得到的二進制檔案會非常大.

- 通過命令 du -sh a.out 與 file a.out 查看檔案大小與屬性:

3. 動態庫
3.1 什么是動態庫
- 動態庫的命名方式與靜態庫類似, 前綴相同為"lib", Linux下后綴名為".so"即libxxxx.so, 而Windows下后綴名為".dll"即"libxxx.dll".
- 鏈接時間: 動態庫在編譯的時候并沒有被編譯進目標代碼, 而是當你的程式執行到相關函式時才呼叫該函式庫里的相應函式. 這樣做的缺點時因為函式庫并沒有整合行程式, 所以程式的運行環境必須提供相應的庫. 優點時動態庫的改變并不會影響你的程式, 所以動態函式庫升級比較方便.
- 編譯鏈接: 動態庫在程式編譯時并不會被鏈接到目標代碼中, 而是在程式運行時才被載入, 因為可執行檔案體積較小. 有了動態庫, 程式的升級會相對比較簡單, 比如某個動態庫升級了, 只需要更換這個動態庫的檔案, 而不需要去更換可執行檔案. 但要注意的是, 可執行程式在運行時需要能找到動態庫檔案.
3.2 動態庫的制作
- 如果希望把原始碼file1.c, file2.c …fileN.c 做成庫檔案, 我們可以通過以下命令把他們制作成動態庫.
gcc -shared -fPIC -o libname.so file1.c file2.c …fileN.c
生成的libashared.a為動態庫, 將他拷貝到’/home/gyp/mylib/shared_libraries/lib’ 路徑下. 將’mylib/main’ 路徑下的動態庫洗掉.

3.3 動態庫的使用
- 編譯與運行:
$ gcc main.c -I …/share_libraries/include/ -L …/share_libraries/lib/ -lashared
此時報錯, 這個錯誤在下面會進行解決.
- 通過命令 du -sh a.out 與 file a.out 查看檔案大小與屬性:

3.4 程式運行報錯解決方法
- 錯誤: error while loading shared libraries: libashared.so: cannot open shared object file: No such file or directory
- 這里為什么說聯結器ld提示找不到庫檔案呢? 這是因為程式運行時沒有找到元件造成的. 這時一定會有人產生疑問, 我們在編譯時不是使用’-L’ 指定動態庫的路徑了嗎, 為什么運行時說我們找不到動態庫呢?
- 程式編譯時鏈接動態庫和運行時使用元件的概念是不同的, 在運行時, 程式鏈接的元件需要在系統目錄下才行. 系統目錄包括( /lib、/lib64、/usr/lib以LD_LIBRARY_PATH環境變數指定的路徑).
- 解決方法如下:
- 將需要的動態庫libashread.so 拷貝到 /lib或/lib64或/usr/lib下(需要有root權限哦)
- 通過export修改LD_LIBRARY_PATH環境變數指定要查找庫的位置
$ export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:your/lib/path(絕對路徑) 臨時生效

- 在編譯時添加 -Wl,-rpath,/your/lib/path/ (-Wl,-R /your/lib/path/).
在gcc中使用ld鏈接選項時,需要在選項前面加上前綴-Wl(小寫L), -R(或-rpath)指定程式運行時庫的路徑.
注意: 這個方法優點是不需要更改環境變數或者拷貝動態庫到系統路徑下, 它的缺點是只要更改了動態庫, 那么就需要重新編譯.

轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/277776.html
標籤:其他
