我們在編寫一個C語言程式的時候,經常會遇到好多重複或常用的部分,如果每次都重新編寫固然是可以的,不過那樣會大大降低工作效率,並且影響代碼的可讀性,更不利於後期的代碼維護。我們可以把他們製作成相應的功能函數,使用時直接調用就會很方便,還可以進行後期的功能更新。
例如我要在一段代碼中多次交換兩個變數的值,我可以在代碼中多次寫入
i=x;
x=y;
y=i;
不過這樣未免有點麻煩我們可以編寫一個change_two_int()函數進行簡化。
定義如下函數:
void change_two_int(int *a,int *b)
{
int c;
c=*a;
*a=*b;
*b=c;
}
這樣每次要進行交換時只需調用 change_two_int(&x , &y);即可,是否方便了許多?
那麼我們要討論的和這些有什麼關係呢?庫通俗的說就是把這些常用函數的目標檔案打包在一起,提供相應函數的介面,便於程式員使用。庫是別人寫好的現有的,成熟的,可以複用的代碼,我們只需要知道其介面如何定義,便可以自如使用。
現實中每個程式都要依賴很多基礎的底層庫,不可能每個人的代碼都從零開始,因此庫的存在意義非同尋常。比如我們常使用的printf函數,就是c標準庫提供的函數。我們在使用時只需要包含相應的標頭檔就可以使用(非靜態編譯還要有相應的庫檔案)。而不用關心printf函數具體是如何?的,這樣就大大提高了程式員編寫代碼的效率。從使用方法上分庫大體上可以分為兩類:靜態庫和共用庫。在windows中靜態庫是以 .lib 為尾碼的檔案,共用庫是以 .dll 為尾碼的檔案。在linux中靜態庫是以 .a 為尾碼的檔案,共用庫是以 .so為尾碼的檔案。
以linux下的靜態庫和動態庫為例我們研究一下,首先我們看一下他們的產生方式
靜態庫:
首先將源檔案編譯成目標檔案:gcc –c a.c b.c
產生靜態庫:ar –rc libstatic.a a.o b.o
共用庫:
同靜態庫一樣編譯成目標檔案:gcc –c a.c b.c
產生共用庫:gcc –fPIC –shared –o libshared.so a.o b.o
由此可見靜態庫和動態庫都是對目標檔案的處理,也可以說庫檔案已經是機器碼檔案了,靜態庫和共用庫的載入過程有很大的區別。
靜態庫的連結方法:
gcc –o staticcode –L. –lstatic main.c –static(預設庫在當前檔案夾)
共用庫的連結方法:
gcc –o sharedcode -L. –lshared main.c(預設庫在當前檔案夾)
當程式與靜態庫串連時,庫中目標檔案所含的所有將被程式使用的函數的機器碼被copy到最終的可執行檔中。這就會導致最終產生的可執行代碼量相對變多,相當於編譯器將代碼補充完整了,這樣運行起來相對就快些。不過會有個缺點: 佔用磁碟和記憶體空間. 靜態庫會被添加到和它串連的每個程式中, 而且這些程式運行時, 都會被載入到記憶體中. 無形中又多消耗了更多的記憶體空間.
與共用庫串連的可執行檔只包含它需要的函數的參考資料表,而不是所有的函數代碼,只有在程式執行時, 那些需要的函數代碼才被拷貝到記憶體中。這樣就使可執行檔比較小, 節省磁碟空間,更進一步,作業系統使用虛擬記憶體,使得一份共用庫駐留在記憶體中被多個程式使用,也同時節約了記憶體。不過由於運行時要去連結庫會花費一定的時間,執行速度相對會慢一些,總的來說靜態庫是犧牲了空間效率,換取了時間效率,共用庫是犧牲了時間效率換取了空間效率,沒有好與壞的區別,只看具體需要了。
另外,.一個程式編好後,有時需要做一些修改和最佳化,如果我們要修改的剛好是庫函數的話,在介面不變的前提下,使用共用庫的程式只需要將共用庫重新編譯就可以了,而使用靜態庫的程式則需要將靜態庫重新編譯好後,將程式再重新編譯一便。
庫操作的相關命令
nm
功能:
列出編入目標檔案或二進位檔案的所有符號。用途一:查看程式調用什麼函數;用 途二:查看一個給定的庫或目標檔案是否提供了所需的函數。
文法:nm [options] file
常用選項:
-C 將符號名轉換為使用者級的名字。在讓C++函數名可讀方面特別有用。
-s 當用於.a檔案時,輸出把符號名映射到定義該符號的模組或成員名的索引。
-u 只顯示未定義的符號,即在被檢查的檔案外部定義的檔案。
-l 使用調試資訊輸出定義每個符號的行號,或未定義符號的重要位項。
ar
功能:將多個.o檔案組合到一起成為.a檔案。
文法:ar [options] lib*.a *.o
常用選項:
-c 如果封存檔案不存在,則建立,並不顯示ar發出的警告。
-q 把*.o添加到封存檔案末尾而不檢查是否進行替換。
-r 向封存檔案中插入.o檔案,替換已有的任何同名檔案,新成員添加到文檔末尾。
-s 建立或升級從符號到.a檔案之間的交叉索引映射表,並加入到.a檔案中。
等價與ranlib [*.a]。執行該命令後,可用nm –s來查看產生的索引。
ldd
功能:顯示可執行程式運行所需的共用庫。
文法
ldd [options] file
常用選項:
-d 執行重定位並報告所有丟失的函數。
-r 執行對函數和資料對象的重定位並報告丟失的任何函數或資料對象。
ldconfig
功能:
在預設搜尋目錄(/lib和/usr/lib)及動態庫設定檔/etc/ld.so.conf中所列的目錄下,搜尋出可共用的動態連結程式庫(lib*.so*),進而建立出動態裝入程式(ld.so)所需的串連和快取檔案。快取檔案預設為 /etc/ld.so.cache,此檔案儲存了已排好序的動態連結程式庫名字列表。該在系統啟動時會運行,而當使用者安裝了一個新的動態連結程式庫時,就需要手工運行這個命令。
文法:
ldconfig [options] path
例如:ldconfig /root/lib 讓系統共用/root/lib目錄下的動態連結程式庫,即在/etc/ld.so.cache中添加指定目錄下的共用庫。[注意]若該目錄不在/lib,/usr/lib,/etc/ld.soconf所列的目錄列表裡,則再次運行ldconf時,此目錄下的動態連結程式庫就不被系統共用了。
常用選項:
-v 更新/etc/ld.so.cache的內容,列處每個庫的版本號碼,掃描的目錄和所有建立和更新的連結。
-p 僅顯示/etc/ld.so.cache的內容,即ld.so所知道的共用庫的當前列表。
-n ldconf僅掃描-n命令所指定的目錄
-f CONF 指定動態連結程式庫的設定檔為CONF,系統預設為/etc/ld.so.conf。
-c CACHE 指定產生的快取檔案為CACHE,系統預設為/etc/ld.so.cache。
當ldconf不帶選項時,僅更新高速緩衝檔案。
環境變數
$LD_PRELOAD 由空格分隔的共用庫列表,在其它庫之前載入,使它們有機會覆蓋或重新定義標準庫。
$LD_LIBRARY_PATH 由冒號分隔的目錄清單,都是共用庫搜尋時會訪問的目錄。
來源: http://www.programbbs.com/bbs/view35-23657-1.htm