在編程的過程中,使用已經封裝好的庫函數是十分方便的,也是十分高效的,因此會使用函數庫是很重要的。在C語言中,函數庫文件分為兩種類型,一種是靜態庫(庫程序是直接注入目標程序的,不分彼此,庫文件通常以.a結尾),另一種是動態庫(庫程序是在運行目標程序時(中)加載的,庫文件通常以.so結尾),下面我們就探索一下這兩種庫文件的特點和使用方式吧!

前言
我們要先了解一下源文件到可執行文件的編譯過程如下圖:

預編譯
預編譯是使用預編譯器cpp進行處理.c源文件和.h頭文件,最終生成一個.i的文件。預編譯過程就是處理源代碼中以#開頭的預編譯指令,如#include #define 等。預編譯過程等價於如下命令:
gcc -E hello.c -o hello.i
或則
cpp hello.c > hello.i
#include 就是將包含的頭文件全部展開到#include的位置,所以一個.c源文件如果包含多個頭文件,頭文件的順序是需要注意的地方。
編譯
編譯的過程就是將 預處理 完的文件進行一系列的詞法分析、語法分析、語義分析及優化,最后生成 .s 匯編代碼文件。編譯過程等價如下命令:
gcc -S hello.i -o hello.s
編譯過程是整個程序構建的核心部分,也是最復雜的部分之一。
匯編
匯編器是將匯編代碼轉變成機器可以執行的指令, 每一條匯編代碼幾乎都對應着一條機器指令。最后生成一個 .o 目標文件。匯編過程等價如下命令:
gcc -c hello.s -o hello.o 或者 as hello.s -o hello.o
匯編器的匯編過程相對簡單一些,只需要根據匯編指令和機器指令對照表一一翻譯就可以了。
鏈接
鏈接的作用就是我們這篇文章的重點,就是將我們編譯出來的目標文件和我們代碼所用到的庫文件一起打包成一個可執行文件的過程。例如hello.c中的打印函數printf,這個函數不是憑空出現的,在鏈接的過程中就要連同對應庫文件一起打包,最終可執行文件才能正常運行。

靜態庫VS動態庫
靜態庫和動態庫的載入時間是不一樣的。
靜態庫的代碼在編譯的過程中已經載入到可執行文件中,所以最后生成的可執行文件相對較大。
動態庫的代碼在可執行程序運行時才載入內存,在編譯過程中僅簡單的引用,所以最后生成的可執行文件相對較小。
靜態庫和動態庫的最大區別是,靜態庫鏈接的時候把庫直接加載到程序中,而動態庫鏈接的時候,它只是保留接口,將動態庫與程序代碼獨立,這樣就可以提高代碼的可復用度和降低程序的耦合度。
靜態庫在程序編譯時會被連接到目標代碼中,程序運行時將不再需要該靜態庫。
動態庫在程序編譯時並不會被連接到目標代碼中,而是在程序運行是才被載入,因此在程序運行時還需要動態庫存在。
無論靜態庫,還是動態庫,都是由.o文件創建的。因此,我們必須將源程序hello.c通過gcc先編譯成.o文件。

靜態庫
靜態庫的名字一般是libxxx.a 在編譯的時候直接編譯進可執行文件中,運行環境中可以不用存在庫文件,但是如果庫文件更新了,可執行文件需要重新編譯。
操作靜態庫
Linux下使用ar命令進行操作靜態庫:
ar archivefile objfile
archivefile:archivefile是靜態庫的名稱
objfile: objfile是已.o為擴展名的中間目標文件名,可以多個並列
參數 意義
-r 將objfile文件插入靜態庫尾或者替換靜態庫中同名文件
-x 從靜態庫文件中抽取文件objfile
-t 打印靜態庫的成員文件列表
-d 從靜態庫中刪除文件objfile
-s 重置靜態庫文件索引
-v 創建文件冗余信息
-c 創建靜態庫文件
編譯靜態庫
在編譯成靜態庫之前,我們需要將源文件編譯一下,生成一個 .o 文件的目標文件。例如寫了一個打印helloworld的接口,我們要先執行:
gcc -c hello.c
ar crv libhello.a hello.o
第一條命令是生成目標文件hello.o ,第二條命令是將目標文件hello.o 打包成靜態庫文件libhello.a。
鏈接靜態庫
上面我們說了靜態庫是如何生成的,然后我們說一下靜態庫怎么使用。靜態庫是要編譯進可執行文件的,在程序運行的環境中,並不需要靜態庫的存在。比如我們生成的靜態庫文件是libhello.a 需要編譯的文件是main.c。編譯命令如下:
gcc main.c -L . -lhello
解讀一下: -L 后面是靜態庫文件所在的目錄,我這里 . 就是指當前目錄的意思。也就是庫文件就和源文件在同一路徑。真正編譯的時候,這個路徑還是要填絕對路徑要好,這個需要注意一下。后面的-l加上庫名,這個庫名是去掉lib和后面的.a。靜態庫的鏈接就是這樣的。

動態庫
動態庫中的代碼是可執行文件在運行中加載執行的,也就是說 程序運行環境中要有動態庫文件。一般動態庫文件命名為lib***.so。動態庫的優點就是方便升級,動態庫變化了,可執行文件不用重新編譯。
編譯動態庫
還拿hello.c來說,使用下面的命令就可以生成一個動態庫文件libhello.so 。看一下各個參數的含義。
gcc -fPIC -shared -o libhello.so hello.c
-fPIC 是創建與地址無關的編譯程序(pic,position independent code),是為了能夠在多個應用程序間共享。-shared指定生成動態鏈接庫。
調用動態庫態庫
我們在運行環境中直接運行可執行文件,前提動態庫文件也在運行環境中。需要注意的是 系統在運行程序的時候,需要知道動態庫的名稱和位置,這樣才能加載,如果找不到動態庫就會直接程序退出報錯。
所以在編譯程序的時候使用下面方式編譯:
gcc mian.c -o mian -L ./ -lhello
同樣,-L后面是庫文件的路徑,最好是用絕對路徑。-l加上去掉lib的庫名。然后直接執行可執行文件就可以了。
還用一種使用動態庫的方式是:
linux提供dlopen、dlsym、dlerror和dlcolose函數獲取動態鏈接庫的函數。通過這個四個函數可以實現一個插件程序,方便程序的擴展和維護。函數格式如下所示:
#include <dlfcn.h>
void *dlopen(const char *filename, int flag);
char *dlerror(void);
void *dlsym(void *handle, const char *symbol);
int dlclose(void *handle);
Link with -ldl.
就是使用這些函數去找對應的庫函數入口地址,然后去執行。

最后,如果你也想成為程序員,想要快速掌握編程,趕緊加入學習企鵝圈子!
里面有資深專業軟件開發工程師,在線解答你的所有疑惑~編程語言入門“so easy”
編程學習書籍:

編程學習視頻:
