




版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請進行舉報或認領
文檔簡介
1、.Linux靜態(tài)and動態(tài)鏈接庫創(chuàng)建和使用目 錄1.概述42.靜態(tài)庫與動態(tài)庫43.靜態(tài)庫的編寫和使用43.1概述43.2編寫最簡單的靜態(tài)庫文件53.3制作庫文件53.3.1生成目標文件53.3.2用ar命令歸檔,格式為ar -rc53.4使用庫文件53.4.1編寫一個測試程序main.c53.4.2編譯目標文件53.4.3生成可執(zhí)行文件63.4.4執(zhí)行可執(zhí)行文件查看效果64.動態(tài)庫的編寫和使用64.1概述64.2編寫最簡單的動態(tài)庫文件64.3編譯生成動態(tài)庫 ,庫文件名以lib開頭, 以.so 結(jié)尾。74.4動態(tài)庫的隱式調(diào)用74.4.1編寫測試文件74.4.2編譯測試程序,與靜態(tài)庫類似,要把頭文
2、件的路徑加到-I參數(shù)里面74.4.3連接生成測試程序74.4.4執(zhí)行測試程序74.5使動態(tài)庫被系統(tǒng)共享的三種辦法84.5.1方法一:拷貝動態(tài)鏈接庫到系統(tǒng)共享目錄下,或在系統(tǒng)共享目錄下為該動態(tài)鏈接庫建立連接(硬連接或符號連接均可,常用符號連接)。84.5.2方法二:將動態(tài)鏈接庫所在目錄名追加到動態(tài)鏈接庫配置文件/etc/ld.so.conf中。84.5.3方法三:利用動態(tài)鏈接庫管理命令ldconfig,強制其搜索指定目錄,并更新緩存文件,便于動態(tài)裝入。84.5.4注意:84.5.5lconfig命令說明84.6動態(tài)庫的顯式調(diào)用94.6.1編寫測試文件104.6.2編譯測試文件 使用-ldl選項指
3、明生成的對象模塊需要使用共享庫104.6.3執(zhí)行測試程序114.6.4顯式調(diào)用例子程序2114.6.5相關函數(shù)的說明如下:124.7相關的命令134.7.1nm命令-查看庫中的符號134.7.2ldd命令-檢查程序都使用到哪些共享庫134.8使用動態(tài)庫時應注意的其他問題144.8.1動態(tài)庫的路徑設置144.8.2同名的靜態(tài)和動態(tài)庫的連接順序141. 概述我們在實際編程工作中肯定會遇到這種情況:有幾個項目里有一些函數(shù)模塊的功能相同,實現(xiàn)代碼也相同,也是我們所說的重復代碼。比如,很多項目里都有一個用戶驗證的功能。代碼段如下: /UserLogin.h文件,提供函數(shù)聲明
4、60; int IsValidUser(char* username, int namelen); /UserLogin.c文件,實現(xiàn)對用戶信息的驗證 int IsValidUser(char* username, int namelen) int IsValid = 0; /*下面是具體的處理代碼,略去*/ return IsValid
5、0; 如果每個項目都保存著這兩個UserLogin.h和UserLogin.c文件,會有以下幾個弊端:1、每個項目里都有重復的模塊,造成代碼重復。 2、代碼的重用性不好,一旦IsValidUser的代碼發(fā)生了變化,為了保持設計的一致性,我們還要手工修改其他項目里的UserLogin.c文件,既費時又費力,還容易出錯。庫文件就是對公共代碼的一種組織形式。為了解決上面兩個弊端,就提出了用庫文件存放公共代碼的解決方案,其要點就是把公共的(也就是可以被多次復用的)目標代碼從項目中分離出來,統(tǒng)一存放到庫文件中,項目要用到這些代碼的時候,在編譯或者運行的時候從庫文件中取得目標代碼即可
6、。庫文件又分兩種:靜態(tài)庫和動態(tài)庫。2. 靜態(tài)庫與動態(tài)庫如果程序是在編譯時加載庫文件的,就是使用了靜態(tài)庫。如果是在運行時加載目標代碼,就成為動態(tài)庫。換句話說,如果是使用靜態(tài)庫,則靜態(tài)庫代碼在編譯時就拷貝到了程序的代碼段,程序的體積會膨脹。如果使用動態(tài)庫,則程序中只保留庫文件的名字和函數(shù)名,在運行時去查找?guī)煳募秃瘮?shù)體,程序的體積基本變化不大。靜態(tài)庫的原則是“以空間換時間”,增加程序體積,減少運行時間;動態(tài)庫則是“以時間換空間”,增加了運行時間,但減少了程序本身的體積。3. 靜態(tài)庫的編寫和使用3.1 概述 靜態(tài)庫文件的擴展名一般為.a,其編寫步驟很簡單。編寫函數(shù)代碼編譯生成各目標文件用a
7、r文件對目標文件歸檔,生成靜態(tài)庫文件。注意歸檔文件名必須以lib打頭。使用要點:在gcc 的-I參數(shù)后加上靜態(tài)庫頭文件的路徑。在gcc 的-L參數(shù)后加上庫文件所在目錄在gcc 的-l參數(shù)后加上庫文件名,但是要去掉lib和.a擴展名。比如庫文件名是libtest.a 那么參數(shù)就是 -l test3.2 編寫最簡單的靜態(tài)庫文件編寫如下兩個文件,注意放在同一目錄中myalib.h /靜態(tài)庫頭文件myalib.c /靜態(tài)庫實現(xiàn)文件 /myalib.h 文件的內(nèi)容void test();/myalib.c 文件的
8、內(nèi)容#inlcude void test() printf("testn");3.3 制作庫文件3.3.1 生成目標文件gcc -c myalib.c 執(zhí)行完后會生成一個myalib.o文件3.3.2 用ar命令歸檔,格式為ar -rc 再次提醒,歸檔文件名一定要以lib打頭, .a結(jié)尾。 ar -rc libtest.a myalib.o 執(zhí)行完后會生成一個libtest.a文件3.4 使用庫文件 3.4.1 編寫一個測試程序main.c內(nèi)容為/main.
9、c 測試靜態(tài)庫調(diào)用的程序#include "myalib.h" /要把函數(shù)的頭文件包含進來,否則編譯時會報錯 int main(int argc,char* argv) test(); return 0;3.4.2 編譯目標文件注意:要把靜態(tài)庫頭文件的路徑加到-I參數(shù)里面gcc -I /root/exercise -o main.o -c main.c現(xiàn)在生成了一個main.o文件3.4.3 生成可執(zhí)行文件注意:要把靜態(tài)庫文件的路徑加到-L參數(shù)里面,把庫文件名(去掉打頭的lib和結(jié)尾的
10、.a)加到-l參數(shù)后面。如下面所示gcc -o main -L/root/exercise main.o ltest此時就會生成一個名為main的可執(zhí)行文件另外,注意- l參數(shù)好象應該加到輸入文件名的后面,否則會報錯。3.4.4 執(zhí)行可執(zhí)行文件查看效果執(zhí)行./main, 輸出 test,說明執(zhí)行成功。 4. 動態(tài)庫的編寫和使用4.1 概述 動態(tài)庫一般以.so結(jié)尾,就是shared object的意思. 其基本生成步驟為1) 編寫函數(shù)代碼2) 編譯生成動態(tài)庫文件,要加上 -shared 和 -fp
11、ic 選項 ,庫文件名以lib開頭, 以.so 結(jié)尾。使用方式分為兩種: 隱式調(diào)用和顯式調(diào)用 :隱式調(diào)用類似于靜態(tài)庫的使用,但需修改動態(tài)鏈接庫的配置文件/etc/ld.so.conf;顯示調(diào)用則是在主程序里使用dlopen、dlsym、dlerror、dlclose等系統(tǒng)函數(shù)。生成動態(tài)庫用gcc來完成,由于可能存在多個版本,因此通常指定版本號: $gcc -shared -Wl,-soname,libhello.so.1 -o libhello.so.1.0 hello.o 另外再建立兩個符號連接: $ln -s libhello.so.1.0 libh
12、ello.so.1$ln -s libhello.so.1 libhello.so這樣一個libhello的動態(tài)連接庫就生成了。最重要的是傳gcc -shared 參數(shù)使其生成是動態(tài)庫而不是普通執(zhí)行程序。 -Wl 表示后面的參數(shù)也就是-soname,libhello.so.1直接傳給連接器ld進行處理。實際上,每一個庫都有一個soname,當連接器發(fā)現(xiàn)它正在查找的程序庫中有這樣一個名稱,連接器便會將soname嵌入連結(jié)中的二進制文件內(nèi),而不是它正在運行的實際文件名,在程序執(zhí)行期間,程序會查找擁有 soname名字的文件,而不是庫的文件名,換句話說,soname是庫的區(qū)分標志。
13、0;這樣做的目的主要是允許系統(tǒng)中多個版本的庫文件共存,習慣上在命名庫文件的時候通常與soname相同 。libxxxx.so.major.minor其中,xxxx是庫的名字,major是主版本號,minor 是次版本號具體的調(diào)用方式會在 "五、動態(tài)庫的調(diào)用" 中詳細說明.4.2 編寫最簡單的動態(tài)庫文件為了便于對照, 我們?nèi)匀徊捎渺o態(tài)庫中的文件做例子。編寫如下兩個文件,注意放在同一目錄中 myalib.h /靜態(tài)庫頭文件myalib.c /靜態(tài)庫實現(xiàn)文件/myalib.h 文件的內(nèi)容vo
14、id test();/myalib.c 文件的內(nèi)容#inlcude void test() printf("testn");4.3 編譯生成動態(tài)庫 ,庫文件名以lib開頭, 以.so 結(jié)尾。gcc -fpic -shared -o libtest.so myalib.c此時就生成一個libtest.so文件4.4 動態(tài)庫的隱式調(diào)用隱式調(diào)用的含義是代碼里不出現(xiàn)庫文件名,就是說這個代碼和調(diào)用靜態(tài)庫的代碼是類似的。4.4.1 編寫測試文件/main.c 測試動態(tài)庫隱式調(diào)用的程序#include "
15、;myalib.h" /要把函數(shù)的頭文件包含進來,否則編譯時會報錯int main(int argc,char* argv) test(); return 0;4.4.2 編譯測試程序,與靜態(tài)庫類似,要把頭文件的路徑加到-I參數(shù)里面gcc -I /root/exercise -o main.o -c main.c現(xiàn)在生成了一個main.o文件4.4.3 連接生成測試程序gcc -o main -L/root/exercise main.o ltest現(xiàn)在生成了一個main文件4.4.4 執(zhí)行測試程序./main
16、60;此時出現(xiàn)提示./main: error while loading shared libraries: libtest.so: cannot open shared object file: No such file or directory。 這個原因就是程序運行時并不知道動態(tài)庫所在的路徑,因此自然找不到。解決這個問題的辦法有三種。見下節(jié)4.5 使動態(tài)庫被系統(tǒng)共享的三種辦法4.5.1 方法一:拷貝動態(tài)鏈接庫到系統(tǒng)共享目錄下,或在系統(tǒng)共享目錄下為該動態(tài)鏈接庫建立連接(硬連接或符號連接均可,常用符號連接)。這里說的系統(tǒng)共享目錄,指的是LINUX動態(tài)鏈接庫存放的目錄,包括/lib
17、,/usr/lib以及/etc/ld.so.conf文件內(nèi)所列的一系列目錄.實例:執(zhí)行# cp libtest.so /lib# ldconfig 或:# ln -s pwd/libtest.so /lib# ldconfig注意:pwd前后有兩個反引號,其目的是取得pwd命令的輸出,即當前目錄.此時再執(zhí)行main,即可成功.4.5.2 方法二:將動態(tài)鏈接庫所在目錄名追加到動態(tài)鏈接庫配置文件/etc/ld.so.conf中。# pwd >> /etc/ld.so.conf # ldconfig 此時再執(zhí)行main,即可成功.4.
18、5.3 方法三:利用動態(tài)鏈接庫管理命令ldconfig,強制其搜索指定目錄,并更新緩存文件,便于動態(tài)裝入。# ldconfig pwd 此時再執(zhí)行main,即可成功.4.5.4 注意:第三種方法雖然有效,但效果是暫時的,供程序測試還可以,一旦再度運行l(wèi)dconfig,文件內(nèi)容可能改變,所需的動態(tài)鏈接庫可能不被系統(tǒng)共享了。而且無論哪種辦法,其實質(zhì)都是用ldconfig命令把動態(tài)庫文件。所在路徑加入到系統(tǒng)庫列表中,(前兩種永久,第三種臨時) 。4.5.5 ldconfig命令說明ldconfig是一個動態(tài)鏈接庫管理命令4.5.5.1 路徑ldconfig在/sbin里面。4.5
19、.5.2 用途linux下的共享庫機制采用了類似于高速緩存的機制,將庫信息保存在/etc/ld.so.cache里邊。程序連接的時候首先從這個文件里邊查找,然后再到ld.so.conf的路徑里邊去詳細找。這就是為什么修改了ld.so.conf要重新運行一下ldconfig的原因ldconfig 命令的用途,主要是在默認搜尋目錄(/lib和/usr/lib)以及動態(tài)庫配置文件/etc/ld.so.conf內(nèi)所列的目錄下,搜索出可共享的動態(tài) 鏈接庫(格式如前介紹,lib*.so*),進而創(chuàng)建出動態(tài)裝入程序(ld.so)所需的連接和緩存文件.緩存文件默認為 /etc/ld.s
20、o.cache,此文件保存已排好序的動態(tài)鏈接庫名字列表.4.5.5.3 用法ldconfig通常在系統(tǒng)啟動時運行,而當用戶安裝了一個新的動態(tài)鏈接庫時,就需要手工運行這個命令.ldconfig命令行用法如下:ldconfig -v|-verbose -n -N -X -f CONF -C CACHE -r ROOT -l -p|-print-cache -c FORMAT -format=FORMAT -V -?|-help|-usage
21、 path.ldconfig可用的選項說明如下:(1) -v或-verbose : 用此選項時,ldconfig將顯示正在掃描的目錄及搜索到的動態(tài)鏈接庫,還有它所創(chuàng)建的連接的名字.(2) -n : 用此選項時,ldconfig僅掃描命令行指定的目錄,不掃描默認目錄(/lib,/usr/lib),也不掃描配置文件/etc/ld.so.conf所列的目錄.(3) -N : 此選項指示ldconfig不重建緩存文件(/etc/ld.so.cache).若未用-X選項,ldconfig照常更新文件的連接
22、.(4) -X : 此選項指示ldconfig不更新文件的連接.若未用-N選項,則緩存文件正常更新.(5) -f CONF : 此選項指定動態(tài)鏈接庫的配置文件為CONF,系統(tǒng)默認為/etc/ld.so.conf.(6) -C CACHE : 此選項指定生成的緩存文件為CACHE,系統(tǒng)默認的是/etc/ld.so.cache,此文件存放已排好序的可共享的動態(tài)鏈接庫的列表.(7) -r ROOT : 此選項改變應用程序的根目錄為ROOT(是調(diào)用ch
23、root函數(shù)實現(xiàn)的).選擇此項時,系統(tǒng)默認的配置文件 /etc/ld.so.conf,實際對應的為 ROOT/etc/ld.so.conf.如用-r /usr/zzz時,打開配置文件 /etc/ld.so.conf時,實際打開的是/usr/zzz/etc/ld.so.conf文件.用此選項,可以大大增加動態(tài)鏈接庫管理的靈活性.(8) -l : 通常情況下,ldconfig搜索動態(tài)鏈接庫時將自動建立動態(tài)鏈接庫的連接.選擇此項時,將進入專家模式,需要手工設置連接.一般用戶不用此項.(9) -p或-print-cache :
24、60;此選項指示ldconfig打印出當前緩存文件所保存的所有共享庫的名字.(10) -c FORMAT 或 -format=FORMAT : 此選項用于指定緩存文件所使用的格式,共有三種: ld(老格式),new(新格式)和compat(兼容格式,此為默認格式).(11) -V : 此選項打印出ldconfig的版本信息,而后退出.(12) -? 或 -help 或 -usage : 這三個選項作用相同,都是讓ldconfi
25、g打印出其幫助信息,而后退出.4.6 動態(tài)庫的顯式調(diào)用顯式調(diào)用的含義是代碼出現(xiàn)庫文件名,用戶需要自己去打開和管理庫文件。其要點為:3) dlfcn.h系統(tǒng)頭文件包含進來4) 用dlopen函數(shù)打開庫文件,并指定打開方式5) 用dlerror()函數(shù)測試是否打開成功,并進行錯誤處理;6) 用dlsym獲得函數(shù)地址,存放在一個函數(shù)指針中7) 用獲得的函數(shù)指針進行函數(shù)調(diào)用。8) 程序結(jié)束時用dlclose關閉打開的動態(tài)庫,防止資源泄露。9) 用ldconfig工具把動態(tài)庫的路徑加到系統(tǒng)庫列表中4.6.1 編寫測試文件/main.c 測試動態(tài)庫顯式調(diào)用的程序#include &
26、#160; /用于動態(tài)庫管理的系統(tǒng)頭文件 #include "myalib.h" /要把函數(shù)的頭文件包含進來,否則編譯時會報錯 int main(int argc,char* argv) /聲明對應的函數(shù)的函數(shù)指針 void (*pTest)(); /加載動態(tài)庫 void *pdlHandle = dlopen("libtest.so", RTLD_LAZY);
27、60; /錯誤處理 if(pdlHandle = NULL ) printf("Failed load libraryn"); return -1; char* pszErr = dlerror();
28、160; if(pszErr != NULL) printf("%sn", pszErr); return -1; /獲取函數(shù)的地址 pTest = dlsym(pdlHandle, "test&qu
29、ot;); pszErr = dlerror(); if(pszErr != NULL) printf("%sn", pszErr); dlclose(pdlHandle); return -1
30、; /實現(xiàn)函數(shù)調(diào)用 (*pTest)(); /程序結(jié)束時關閉動態(tài)庫 dlclose(pdlHandle); return 0; 4.6.2 編譯測試文件 使用-ldl選項指明生成的對象模塊需要使用共享庫gcc -o main -ldl main.c執(zhí)行完后就生成了一個main文件4.6.3 執(zhí)行測試程序執(zhí)行 ./main 輸出test 說明
31、成功。4.6.4 顯式調(diào)用例子程序2/*FileName: main2.cDescription: test static/dynamic libraryAuthor: HCJDate : 2005-5-7*/#include<stdio.h>#include<dlfcn.h>int main(int argc, char* argv) /define function pointor int (*pStrlenFun)(char* pStr);
32、60; /聲明對應的函數(shù)的函數(shù)指針 int (*pStrnlenFun)(char* pStr, int ulMaxLen); char str = "hello world" unsigned long ulLength = 0; void *pdlHandle; char *pszErr; pdlHandle = dlopen("./libstr.
33、so", RTLD_LAZY); /加載鏈接庫/libstr.so if(!pdlHandle) printf("Failed load libraryn"); pszErr = dlerror(); if(pszErr != NULL) &
34、#160; printf("%sn", pszErr); return 0; /get function from lib pStrlenFun = dlsym(pdlHandle, "Strlen"); /獲取函數(shù)的地址 pszErr = dlerror();
35、0; if(pszErr != NULL) printf("%sn", pszErr); return 0; pStrnlenFun = dlsym(pdlHandle, "StrNlen"); pszErr = dle
36、rror(); if(pszErr != NULL) printf("%sn", pszErr); return 0; printf("The string is : %sn", str); ulLength
37、 = pStrlenFun(str); /調(diào)用相關的函數(shù) printf("The string length is : %d(use Strlen)n", ulLength); ulLength = pStrnlenFun(str, 10); printf("The string length is : %d(use StrNlen)n", ulLength); dlclose(pdlHandle); &
38、#160; return 0; gcc -o mian2 -ldl main2.c用gcc編譯對應的源文件生成可執(zhí)行文件,-ldl選項,表示生成的對象模塊需要使用共享庫。執(zhí)行對應得文件同樣可以得到正確的結(jié)果。4.6.5 相關函數(shù)的說明如下:4.6.5.1 dlopen()dlopen的第一個參數(shù)為共享庫的名稱,將會在下面位置查找指定的共享庫。l 環(huán)境變量LD_LIBRARY_PATH列出的用分號間隔的所有目錄。l 文件/etc/ld.so.cache中找到的庫的列表,由ldconfig命令刷新。l 目錄usr/lib。l 目錄/lib。l 當前目錄
39、。第二個參數(shù)為打開共享庫的方式。有兩個取值l RTLD_NOW:將共享庫中的所有函數(shù)加載到內(nèi)存l RTLD_LAZY:會推后共享庫中的函數(shù)的加載操作,直到調(diào)用dlsym()時方加載某函數(shù)4.6.5.2 dlsym()調(diào)用dlsym時,利用dlopen()返回的共享庫的phandle以及函數(shù)名稱作為參數(shù),返回要加載函數(shù)的入口地址。4.6.5.3 dlerror()該函數(shù)用于檢查調(diào)用共享庫的相關函數(shù)出現(xiàn)的錯誤。 這樣我們就用簡單的例子說明了在Linux下靜態(tài)/動態(tài)庫的創(chuàng)建和使用。 4.6.5.4 dlclose()關閉動態(tài)庫.4.7 相關的命令4.7.1 nm命令-
40、查看庫中的符號有時候可能需要查看一個庫中到底有哪些函數(shù),nm命令可以打印出庫中的涉及到的所有符號。庫既可以是靜態(tài)的也可以是動態(tài)的。nm列出的符號有很多,常見的有三種,一種是在庫中被調(diào)用,但并沒有在庫中定義(表明需要其他庫支持),用U表示;一種是庫中定義的函數(shù),用T表示,這是最常見的;另外一種是所謂的“弱態(tài)”符號,它們雖然在庫中被定義,但是可能被其他庫中的同名符號覆蓋,用W表示。例如,假設開發(fā)者希望知道上央提到的hello庫中是否定義了 printf(): $nm libhello.so |grep printfU printfU表示符號printf被引用,但是并沒有在函數(shù)內(nèi)定義,由此
41、可以推斷,要正常使用hello庫,必須有其它庫支持,再使用ldd命令查看hello依賴于哪些庫: $ldd hello libc.so.6=>/lib/libc.so.6(0x400la000) /lib/ld-linux.so.2=>/lib/ld-linux.so.2 (0x40000000) 從上面的結(jié)果可以繼續(xù)查看printf最終在哪里被定義,有興趣可以go on 4.7.2 ldd命令-檢查程序都使用到哪些共享庫4.7.2.1 ldd命令行用法如下: ldd -version -v|-verbose -d|-da
42、ta-relocs -r|-function-relocs -help FILE. 各選項說明如下: (1) -version : 此選項用于打印出ldd的版本號. (2) -v 或 -verbose : 此選項指示ldd輸出關于所依賴的動態(tài)鏈接庫的盡可能詳細的信息. (3) -d 或 -data-relocs : 此選項執(zhí)行重定位,并且顯示不存在的函數(shù). (4) -r 或 -function-relocs : 此選項執(zhí)行數(shù)據(jù)對象與函數(shù)的重定位,同時報告不存在的對象.
43、60; (5) -help : 此選項用于打印出ldd的幫助信息. 我們一般用-v選項.4.7.2.2 實例4.7.2.2.1 用靜態(tài)庫連接時的結(jié)果 #ldd mainlibc.so.6 => /lib/tls/libc.so.6 (0xb74ad000)/lib/ld-linux.so.2 => /lib/ld-linux.so.2 (0xb75eb000)可見使用靜態(tài)庫時,由于庫已經(jīng)被編譯成程序的一部分,因此ldd的輸出中就只有用到的系統(tǒng)庫。4.7.2.2.2 用動態(tài)庫隱式連接時的結(jié)果 li
44、btest.so => /root/exercise/libtest.so (0xb75e2000)libc.so.6 => /lib/tls/libc.so.6 (0xb74ab000)/lib/ld-linux.so.2 => /lib/ld-linux.so.2 (0xb75eb000)可見隱式使用動態(tài)庫時,所有用到的動態(tài)庫(包括系統(tǒng)和用戶的)都會被顯示出來。4.7.2.2.3 動態(tài)庫顯式連接時的結(jié)果ldd mainlibdl.so.2 => /lib/libdl.so.2 (0xb75e1000)libc.so.6 => /lib/tls/libc.so.
45、6 (0xb74aa000)/lib/ld-linux.so.2 => /lib/ld-linux.so.2 (0xb75eb000)可見顯式使用動態(tài)庫時,程序中不再保存運行時打開動態(tài)庫的信息,只保留用到的系統(tǒng)庫的信息. 這個與使用靜態(tài)庫時的輸出是類似的.4.8 使用動態(tài)庫時應注意的其他問題4.8.1 動態(tài)庫的路徑設置無論是動態(tài)庫的顯式調(diào)用還是隱式調(diào)用,都需要用ldconfig工具將動態(tài)庫的路徑加到系統(tǒng)庫列表中,否則運行時會出錯。 為了讓執(zhí)行程序順利找到動態(tài)庫,有三種方法: (1)把庫拷貝到/usr/lib和/lib目錄下。(2)在LD_LIBRARY_PA
46、TH環(huán)境變量中加上庫所在路徑。例如動態(tài)庫libhello.so在/home/ting/lib目錄下,以bash為例,使用命令:$export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/home/ting/lib(3) 修改/etc/ld.so.conf文件,把庫所在的路徑加到文件末尾,并執(zhí)行l(wèi)dconfig刷新。這樣,加入的目錄下的所有庫文件都可見。4.8.2 同名的靜態(tài)和動態(tài)庫的連接順序當要使用靜態(tài)的程序庫時,連接器會找出程序所需的函數(shù),然后將它們拷貝到執(zhí)行文件,由于這種拷貝是完整的,所以一旦連接成功,靜態(tài)程序庫也就不再需要了。然而,對動態(tài)庫而言,就不是這樣。動態(tài)庫
47、會在執(zhí)行程序內(nèi)留下一個標記指明當程序執(zhí)行時,首先必須載入這個庫。由于動態(tài)庫節(jié)省空間,linux下進行連接的缺省操作是首先連接動態(tài)庫,也就是說,如果同時存在靜態(tài)和動態(tài)庫,不特別指定的話,將與動態(tài)庫相連接。4.8.2.1 例子現(xiàn)在假設有一個叫hello的程序開發(fā)包,它提供一個靜態(tài)庫libhello.a 一個動態(tài)庫libhello.so,一個頭文件hello.h,頭文件中提供sayhello()這個函數(shù) /* hello.h */void sayhello();另外還有一些說明文檔。這一個典型的程序開發(fā)包結(jié)構(gòu) 4.8.2.1.1 與動態(tài)庫連接 linux默認的就是與動態(tài)
48、庫連接,下面這段程序testlib.c使用hello庫中的sayhello()函數(shù) /*testlib.c*/#include#include int main() sayhello();return 0; 使用如下命令進行編譯 $gcc -c testlib.c -o testlib.o 用如下命令連接: $gcc testlib.o -lhello -o testlib在連接時要注意,假設libhello.so 和libhello.a都在缺省的庫搜索路徑下/usr/lib下,如果在其它位置要加上-L參數(shù) 4.8
49、.2.1.2 與靜態(tài)庫連接與靜態(tài)庫連接與麻煩一些,主要是參數(shù)問題。還是上面的例子:$gcc testlib.o -o testlib -WI,-Bstatic -lhello 注:這個特別的"-WI,-Bstatic"參數(shù),實際上是傳給了連接器ld.指示它與靜態(tài)庫連接,如果系統(tǒng)中只有靜態(tài)庫當然就不需要這個參數(shù)了。如果要和多個庫相連接,而每個庫的連接方式不一樣,比如上面的程序既要和libhello進行靜態(tài)連接,又要和libbye進行動態(tài)連接,其命令應為: $gcc testlib.o -o testlib -WI,-Bstatic -lhello -WI,
50、-Bdynamic -lbye 5. 其他相關知識5.1 指針函數(shù)和函數(shù)指針的區(qū)別這兩個概念都是簡稱,指針函數(shù)是指帶指針的函數(shù),即本質(zhì)是一個函數(shù)。我們知道函數(shù)都又有返回類型(如果不返回值,則為無值型),只不過指針函數(shù)返回類型是某一類型的指針。5.2 函數(shù)指針5.2.1 概述函數(shù)指針是指向函數(shù)的指針變量。 因而“函數(shù)指針”本身首先應是指針變量,只不過該指針變量指向函數(shù)。這正如用指針變量可指向整型變量、字符型、數(shù)組一樣,這里是指向函數(shù)。如前所述,C在編譯時,每一個函數(shù)都有一個入口地址,該入口地址就是函數(shù)指針所指向的地址。有了指向函數(shù)的指針變量后,可用該指針變量調(diào)用函數(shù),就如同用指針變量可
51、引用其他類型變量一樣,在這些概念上是一致的。函數(shù)指針有兩個用途:調(diào)用函數(shù)和做函數(shù)的參數(shù)。5.2.2 方法函數(shù)指針的聲明方法為: 數(shù)據(jù)類型標志符 (指針變量名) (形參列表); 注1:“函數(shù)類型”說明函數(shù)的返回類型,由于“()”的優(yōu)先級高于“*”,所以指針變量名外的括號必不可少,后面的“形參列表”表示指針變量指向的函數(shù)所帶的參數(shù)列表。例如: int func(int x); /* 聲明一個函數(shù) */ int (*f) (int x); /* 聲明一個函數(shù)指針 */ f=func; /* 將func函數(shù)的首地址賦給指針f */ 賦值時函數(shù)func不帶括號,也不帶參數(shù),由于func代表函數(shù)的首地址,
52、因此經(jīng)過賦值以后,指針f就指向函數(shù)func(x)的代碼的首地址。 注2:函數(shù)括號中的形參可有可無,視情況而定。 下面的程序說明了函數(shù)指針調(diào)用函數(shù)的方法: 5.2.3 例一 #include<stdio.h> int max(int x,int y) return(x>y?x:y); void main() int (*ptr)(int, int); int a,b,c; ptr=max; scanf("%d,%d",&a,&b); c=(*ptr)(a,b); printf("a=%d,b=%d,max=%d",a,b,
53、c); ptr是指向函數(shù)的指針變量,所以可把函數(shù)max()賦給ptr作為ptr的值,即把max()的入口地址賦給ptr,以后就可以用ptr來調(diào)用該函數(shù),實際上ptr和max都指向同一個入口地址,不同就是ptr是一個指針變量,不像函數(shù)名稱那樣是死的,它可以指向任何函數(shù),就看你想怎么做了。在程序中把哪個函數(shù)的地址賦給它,它就指向哪個函數(shù)。而后用指針變量調(diào)用它,因此可以先后指向不同的函數(shù)。不過注意,指向函數(shù)的指針變量沒有+和-運算,用時要小心。 不過,在某些編譯器中這是不能通過的。這個例子的補充如下。 應該是這樣的: 1.定義函數(shù)指針類型: typedef int (*fun_ptr)(int,in
54、t); 2.申明變量,賦值: fun_ptr max_func=max; 也就是說,賦給函數(shù)指針的函數(shù)應該和函數(shù)指針所指的函數(shù)原型是一致的。 5.2.4 例二 #include<stdio.h> void FileFunc() printf("FileFuncn"); void EditFunc() printf("EditFuncn"); void main() typedef void (*funcp)(); funcp=FileFunc; funcp(); funcp=EditFunc; funcp(); 5.2.5 簡單的函數(shù)指針的應
55、用。/形式1:返回類型(*函數(shù)名)(參數(shù)表) char (*pFun)(int); char glFun(int a) return; void main() pFun = glFun; (*pFun)(2); 第一行定義了一個指針變量pFun。首先我們根據(jù)前面提到的“形式1”認識到它是一個指向某種函數(shù)的指針,這種函數(shù)參數(shù)是一個int型,返回值是char類型。只有第一句我們還無法使用這個指針,因為我們還未對它進行賦值。
56、60; 第二行定義了一個函數(shù)glFun()。該函數(shù)正好是一個以int為參數(shù)返回char的函數(shù)。我們要從指針的層次上理解函數(shù)函數(shù)的函數(shù)名實際上就是一個指針,函數(shù)名指向該函數(shù)的代碼在內(nèi)存中的首地址。 然后就是可愛的main()函數(shù)了,它的第一句您應該看得懂了它將函數(shù)glFun的地址賦值給變量pFun。main()函數(shù)的第二句中“*pFun”顯然是取pFun所指向地址的內(nèi)容,當然也就是取出了函數(shù)glFun()的內(nèi)容,然后給定參數(shù)為2。5.2.6 使用typedef更直觀更方便。 /形式2:typedef 返回類型(*新類型)(參數(shù)表)typedef char (*PTRFUN)(int); PTRFUN pFun; ch
溫馨提示
- 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
- 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
- 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會有圖紙預覽,若沒有圖紙預覽就沒有圖紙。
- 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
- 5. 人人文庫網(wǎng)僅提供信息存儲空間,僅對用戶上傳內(nèi)容的表現(xiàn)方式做保護處理,對用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對任何下載內(nèi)容負責。
- 6. 下載文件中如有侵權(quán)或不適當內(nèi)容,請與我們聯(lián)系,我們立即糾正。
- 7. 本站不保證下載資源的準確性、安全性和完整性, 同時也不承擔用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。
最新文檔
- 《浙江森歌公司實施全面預算管理的成效及其經(jīng)驗分析》5600字
- 個人雇傭房東護工協(xié)議書9篇
- 審計鑒定協(xié)議9篇
- 商業(yè)空間租賃協(xié)議簽訂流程與注意事項說明
- 公司債權(quán)債務承擔協(xié)議書范本6篇
- 地質(zhì)期末考試試題及答案
- 十九打考試試題及答案
- 泉州道法考試試題及答案
- 黔南司法考試試題及答案
- 簡單免責協(xié)議書模板
- 感悟考古智慧樹知到期末考試答案章節(jié)答案2024年北京大學
- 2025陜西省高二學業(yè)水平考試數(shù)學模擬試卷試題(含答案詳解)
- DL-T5142-2012火力發(fā)電廠除灰設計技術(shù)規(guī)程
- 2024年全國青少年航天創(chuàng)新大賽航天知識競賽試題
- DB11∕2075-2022 建筑工程減隔震技術(shù)規(guī)程
- 鉛鋅礦的冶煉技術(shù)進展與設備改進
- 煤礦勞動組織管理培訓課件
- 倉儲績效考核實施細則倉庫人員績效考核內(nèi)容與評分標準
- 混凝土拌合物凝結(jié)時間自動計算記錄
- 2022睡眠醫(yī)學中心建設指南
- 地磅允許誤差
評論
0/150
提交評論