gcc/g++實戰之動態鏈接庫與靜態鏈接庫編寫


函數庫一般分為靜態庫和動態庫兩種。

靜態庫:

是指編譯鏈接時,把庫文件的代碼全部加入到可執行文件中,因此生成的文件比較大,但在運行時也就不再需要庫文件了。其后綴名一般為”.a”。

動態庫:

與之相反,在編譯鏈接時並沒有把庫文件的代碼加入到可執行文件中,而是在程序執行時由運行時鏈接文件加載庫,這樣可以節省系統的開銷。動態庫一般后綴名為”.so”,gcc/g++在編譯時默認使用動態庫。無論靜態庫,還是動態庫,都是由.o文件創建的。

 

動態庫的編譯:

下面通過一個例子來介紹如何生成一個動態庫。建一個頭文件:dynamic.h,三個.cpp文件:dynamic_a.cpp、dynamic_b.cpp、dynamic_c.cpp,我們將這幾個文件編譯成一個動態庫:libdynamic.so。
//dynamic.h
#ifndef __DYNAMIC_H_
#define __DYNAMIC_H_
#include <iostream>
void dynamic_a();
void dynamic_b();
void dynamic_c();
#endif

  

//dynamic_a.cpp:
#include"dynamic.h"
void dynamic_a()
{
  cout<<"this is in dynamic_a "<<endl;
}
 
//dynamic_b.cpp:
#include"dynamic.h"
void dynamic_b()
{
  cout<<"this is in dynamic_b "<<endl;
}
 
 
//dynamic_c.cpp:
#include"dynamic.h"
void dynamic_c()
{
  cout<<"this is in dynamic_c "<<endl;
}
 
將這幾個文件編譯成動態庫libdynamic.so。編譯命令如下:
 
g++ dynamic_a.cpp dynamic_b.cpp dynamic_c.cpp -fPIC -shared -o libdynamic.so
 

參數說明:

-shared:該選項指定生成動態連接庫
-fPIC:表示編譯為位置獨立的代碼,不用此選項的話編譯后的代碼是位置相關的所以動態載入時是通過代碼拷貝的方式來滿足不同進程的需要,而不能達到真正代碼段共享的目的。
 
 
在上面的部分,我們已經生成了一個libdynamic.so的動態鏈接庫,現在我們用一個程序來調用這個動態鏈接庫。文件名為:main.cpp
//main.cpp:
#include"dynamic.h"
int main()
{
  dynamic_c();
  dynamic_c();
  dynamic_c();
  return 0;
}

 

將main.cpp與libdynamic.so鏈接成一個可執行文件main。命令如下:
 
g++ main.cpp -L. -ldynamic -o main

 

參數說明:

-L.:表示要連接的庫在當前目錄中
-ldynamic:編譯器查找動態連接庫時有隱含的命名規則,即在給出的名字前面加上lib,后面加上.so來確定庫的名稱
 
 
測試可執行程序main是否已經鏈接的動態庫libdynamic.so,如果列出了libdynamic.so,那么就說明正常鏈接了。可以執行以下命令:
ldd main

 
如果運行:
./main
 
出現錯誤 error while loading shared libraries: libdynamic.so: cannot open shared object file: No such file or directory
 

錯誤原因

ld提示找不到庫文件,而庫文件就在當前目錄中。
 
鏈接器ld默認的目錄是/lib和/usr/lib,如果放在其他路徑也可以,需要讓ld知道庫文件在哪里。
 

解決方法:

方法1:

編輯/etc/ld.so.conf文件,在新的一行中加入庫文件所在目錄;比如筆者應添加:
/home/neu/code/Dynamic_library
 
運行:
sudo ldconfig

 

目的是用ldconfig加載,以更新/etc/ld.so.cache文件;
 
 

方法2:

在/etc/ld.so.conf.d/目錄下新建任何以.conf為后綴的文件,在該文件中加入庫文件所在的目錄;
運行:
sudo ldconfig

以更新/etc/ld.so.cache文件;

 
ld.so.cache的更新是遞增式的,就像PATH系統環境變量一樣,不是從頭重新建立,而是向上累加。除非重新開機,才是從零開始建立ld.so.cache文件。

方法3:

在bashrc或profile文件里用LD_LIBRARY_PATH定義,然后用source加載。

方法4:

你可以直接采用在編譯鏈接的時候告訴系統你的庫在什么地方
 
執行main可以看看main是否調用了動態鏈接庫中的函數。
./main

 

靜態庫的編譯:

讀者可以自己創建代碼,筆者比較懶,就以以上代碼演示,最好把生成的動態庫的東西全部刪掉。

1.編譯靜態庫:
g++ -c dynamic_a.cpp dynamic_b.cpp dynamic_c.cpp  

 

 2.使用ar命令創建靜態庫文件(把目標文檔歸檔)
 
ar cr libstatic.a dynamic_a.o dynamic_b.o dynamic_c.o  //cr標志告訴ar將object文件封裝(archive)

 

 
 
參數說明:
         
          d 從指定的靜態庫文件中刪除文件 
          m 把文件移動到指定的靜態庫文件中 
          p 把靜態庫文件中指定的文件輸出到標准輸出 
          q 快速地把文件追加到靜態庫文件中 
          r 把文件插入到靜態庫文件中 
          t 顯示靜態庫文件中文件的列表 
          x 從靜態庫文件中提取文件 
          a 把新的目標文件(*.o)添加到靜態庫文件中現有文件之后 
 
 
使用nm -s 命令來查看.a文件的內容
nm -s libstatic.a 

 
3.鏈接靜態庫
g++ main.cpp -lstatic -L. -static -o main//這里的-static選項是告訴編譯器,hello是靜態庫也可以用

                        //g++ main.cpp -lstatic -L.  -o main 

 

執行以下命令,因為筆者還是用的動態庫的代碼,所以結果一樣
./main

 

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM