咱們在實際編程中確定會遇到這種狀況:有幾個項目裏有一些函數模塊的功能相同,實現代碼也相同,也是咱們所說的重複代碼。好比,不少項目裏都有一個用戶驗證的功能。
代碼段以下:編程
//UserLogin.h文件,提供函數聲明
int IsValidUser(char* username, int namelen); //UserLogin.c文件,實現對用戶信息的驗證
int IsValidUser(char* username, int namelen) { int IsValid = 0; /*下面是具體的處理代碼,略去*/
return IsValid }
若是每一個項目都保存着這兩個UserLogin.h和UserLogin.c文件,會有如下幾個弊端:緩存
爲了解決上面兩個弊端,就提出了用庫文件存放公共代碼的解決方案,其要點就是把公共的(也就是能夠被屢次複用的)目標代碼從項目中分離出來,統一存放到庫文件中,項目要用到這些代碼的時候,在編譯或者運行的時候從庫文件中取得目標代碼便可。庫文件又分兩種:靜態庫和動態庫。ide
若是程序是在編譯時加載庫文件的,就是使用了靜態庫。若是是在運行時加載目標代碼,就成爲動態庫。換句話說,若是是使用靜態庫,則靜態庫代碼在編譯時就拷貝到了程序的代碼段,程序的體積會膨脹。若是使用動態庫,則程序中只保留庫文件的名字和函數名,在運行時去查找庫文件和函數體,程序的體積基本變化不大。函數
靜態庫文件的擴展名通常爲.a,其編寫步驟很簡單。工具
注意歸檔文件名必須以lib打頭。測試
使用要點:spa
- 在gcc 的-I參數後加上靜態庫頭文件的路徑。
- 在gcc 的-L參數後加上庫文件所在目錄
- 在gcc 的-l參數後加上庫文件名,可是要去掉lib和.a擴展名。
好比庫文件名是libtest.a 那麼參數就是 -l test設計
編寫以下兩個文件,注意放在同一目錄中指針
//lcw_lib.h 文件的內容
void test(); //lcw_lib.c 文件的內容
#inlcude <stdio.h>
void test() { printf("lcw_lib test ! \n"); }
mystery@lcw:~/Desktop/code/lib_test$ gcc -c lcw_lib.c mystery@lcw:~/Desktop/code/lib_test$ ls lcw_lib.c lcw_lib.h lcw_lib.o
執行完後會生成一個lcw_lib.o文件code
再次提醒,歸檔文件名必定要以lib打頭, .a結尾。
mystery@lcw:~/Desktop/code/lib_test$ ar -rc libtest.a lcw_lib.o mystery@lcw:~/Desktop/code/lib_test$ ls lcw_lib.c lcw_lib.h lcw_lib.o libtest.a mystery@lcw:~/Desktop/code/lib_test$
執行完後會生成一個libtest.a文件
//main.c 測試靜態庫調用的程序
#include "lcw_lib.h" //要把函數的頭文件包含進來,不然編譯時會報錯
int main(int argc,char* argv[]) { test(); return 0; }
mystery@lcw:~/Desktop/code/lib_test$ gcc -I `pwd` -o main.o -c main.c mystery@lcw:~/Desktop/code/lib_test$ ls lcw_lib.c lcw_lib.h lcw_lib.o libtest.a main.c main.o mystery@lcw:~/Desktop/code/lib_test$
如今生成了一個main.o文件.
mystery@lcw:~/Desktop/code/lib_test$ gcc -o main -L `pwd` main.o -l test mystery@lcw:~/Desktop/code/lib_test$ ls lcw_lib.c lcw_lib.h lcw_lib.o libtest.a main main.c main.o mystery@lcw:~/Desktop/code/lib_test$
此時就會生成一個名爲main的可執行文件.
執行./main
mystery@lcw:~/Desktop/code/lib_test$ ./main lcw_lib test ! mystery@lcw:~/Desktop/code/lib_test$
OK,測試成功。
動態庫通常以.so結尾,就是shared object的意思.
生成步驟爲
使用方式分爲兩種: 隱式調用和顯示調用
爲了便於對照, 現仍然採用靜態庫中的文件作例子.
mystery@lcw:~/Desktop/code/lib_test$ ls lcw_lib.c lcw_lib.h lcw_lib.o libtest.a main main.c main.o mystery@lcw:~/Desktop/code/lib_test$ rm main.o main libtest.a lcw_lib.o mystery@lcw:~/Desktop/code/lib_test$ ls lcw_lib.c lcw_lib.h main.c mystery@lcw:~/Desktop/code/lib_test$
mystery@lcw:~/Desktop/code/lib_test$ gcc -fpic -shared -o libtest.so lcw_lib.c mystery@lcw:~/Desktop/code/lib_test$ ls lcw_lib.c lcw_lib.h libtest.so main.c mystery@lcw:~/Desktop/code/lib_test$
此時就生成一個libtest.so文件。
隱式調用的含義是代碼裏不出現庫文件名,就是說這個代碼和調用靜態庫的代碼是相似的。
//main.c 測試動態庫隱式調用的程序,與前面的同樣
mystery@lcw:~/Desktop/code/lib_test$ gcc -I `pwd` -o main.o -c main.c mystery@lcw:~/Desktop/code/lib_test$ ls lcw_lib.c lcw_lib.h libtest.so main.c main.o mystery@lcw:~/Desktop/code/lib_test$
如今生成了一個main.o文件。
mystery@lcw:~/Desktop/code/lib_test$ gcc -o main -L `pwd` main.o -l test mystery@lcw:~/Desktop/code/lib_test$ ls lcw_lib.c lcw_lib.h libtest.so main main.c main.o mystery@lcw:~/Desktop/code/lib_test$
如今生成了一個main文件。
mystery@lcw:~/Desktop/code/lib_test$ ./main ./main: error while loading shared libraries: libtest.so: cannot open shared object file: No such file or directory mystery@lcw:~/Desktop/code/lib_test$
出現錯誤,這個緣由就是程序運行時並不知道動態庫所在的路徑,所以天然找不到。解決方法以下。
(1)拷貝動態連接庫到系統共享目錄下,或在系統共享目錄下爲該動態連接庫創建鏈接(硬鏈接或符號鏈接都可,經常使用符號鏈接).這裏說的系統共享目錄,指的是LINUX動態連接庫存放的目錄,包括/lib,/usr/lib以及/etc/ld.so.conf文件內所列的一系列目錄.
實例:執行
# cp libtest.so /lib # ldconfig 或: # ln -s `pwd`/libtest.so /lib # ldconfig
注意pwd先後有兩個反引號`,其目的是取得pwd命令的輸出,即當前目錄.此時再執行main,便可成功.
(2)將動態連接庫所在目錄名追加到動態連接庫配置文件/etc/ld.so.conf中.
# pwd >> /etc/ld.so.conf # ldconfig
此時再執行main,便可成功.
(3)利用動態連接庫管理命令ldconfig,強制其搜索指定目錄,並更新緩存文件,便於動態裝入.
# ldconfig `pwd`
此時再執行main,便可成功.
要注意,第三種方法雖然有效,但效果是暫時的,供程序測試還能夠,一旦再度運行ldconfig,則緩存文件內容可能改變,所需的動態連接庫可能不被系統共享了.
不管哪一種辦法,其實質都是用ldconfig命令把動態庫文件所在路徑加入到系統庫列表中,(前兩種永久,第三種臨時)
顯式調用的含義是代碼出現庫文件名,用戶須要本身去打開和管理庫文件。其要點爲:
dllope的的第一個參數爲共享庫的名稱,將會在下面位置查找指定的共享庫。
第二個參數爲打開共享庫的方式。有兩個取值
//main.c 測試動態庫顯式調用的程序
1 #include<dlfcn.h> //用於動態庫管理的系統頭文件
2 #include "lcw_lib.h" //要把函數的頭文件包含進來,不然編譯時會報錯
3 int main(int argc,char* argv[]) 4 { 5 //聲明對應的函數的函數指針
6 void (*pTest)(); 7 //加載動態庫
8 void *pdlHandle = dlopen("libtest.so", RTLD_LAZY); 9 //錯誤處理
10 if(pdlHandle == NULL ) 11 { 12 printf("Failed load library\n"); 13 return -1; 14 } 15 char* pszErr = dlerror(); 16 if(pszErr != NULL) 17 { 18 printf("%s\n", pszErr); 19 return -1; 20 } 21 //獲取函數的地址
22 pTest = dlsym(pdlHandle, "test"); 23 pszErr = dlerror(); 24 if(pszErr != NULL) 25 { 26 printf("%s\n", pszErr); 27 dlclose(pdlHandle); 28 return -1; 29 } 30 //實現函數調用
31 (*pTest)(); 32 //程序結束時關閉動態庫
33 dlclose(pdlHandle); 34 return 0; 35 }
mystery@lcw:~/Desktop/code/lib_test$ gcc -o main main.c -ldl mystery@lcw:~/Desktop/code/lib_test$ ls lcw_lib.c lcw_lib.h libtest.so main main.c mystery@lcw:~/Desktop/code/lib_test$
執行完後就生成了一個main文件,i注意參數 -ldl 須要放在最後,放在前面時編譯沒法經過