【轉】關於動態庫和靜態庫

參考:http://blog.jobbole.com/86852/linux

因爲我只在windows下使用,linux部分就很少說了,總結一下windows下面的相關知識好了:程序員

靜態庫

之因此成爲【靜態庫】,是由於在連接階段,會將彙編生成的目標文件.o與引用到的庫一塊兒連接打包到可執行文件中。所以對應的連接方式稱爲靜態連接。windows

試想一下,靜態庫與彙編生成的目標文件一塊兒連接爲可執行文件,那麼靜態庫一定跟.o文件格式類似。其實一個靜態庫能夠簡單當作是一組目標文件(.o/.obj文件)的集合,即不少目標文件通過壓縮打包後造成的一個文件。靜態庫特色總結:app

  •  靜態庫對函數庫的連接是放在編譯時期完成的。
  •  程序在運行時與函數庫再無瓜葛,移植方便。
  •  浪費空間和資源,由於全部相關的目標文件與牽涉到的函數庫被連接合成一個可執行文件。

Windows下建立與使用靜態庫

建立靜態庫(.lib框架

若是是使用VS命令行生成靜態庫,也是分兩個步驟來生成程序:函數

  • 首先,經過使用帶編譯器選項 /c 的 Cl.exe 編譯代碼 (cl /c xxx.cpp),建立名爲「xxx.obj」的目標文件。
  • 而後,使用庫管理器 Lib.exe 連接代碼 (lib xxx.obj),建立靜態庫xxx.lib

固然,咱們通常不這麼用,使用VS工程設置更方便。建立win32控制檯程序時,勾選靜態庫類型;打開工程「屬性面板」→」配置屬性」→」常規」,配置類型選擇靜態庫。工具

Build項目便可生成靜態庫。測試

 

使用靜態庫ui

測試代碼Linux下面的同樣。有3種使用方法:spa

方法一:

在VS中使用靜態庫方法:

  • 工程「屬性面板」→「通用屬性」→「框架和引用」→」添加引用」,將顯示「添加引用」對話框。 「項目」選項卡列出了當前解決方案中的各個項目以及能夠引用的全部庫。 在「項目」選項卡中,選擇要添加的庫(工程要在解決方案下?)。 單擊「肯定
  • 添加xxx.h 頭文件目錄,必須修改包含目錄路徑。打開工程「屬性面板」→」配置屬性」→「C/C++」→」 常規」,在「附加包含目錄」屬性值中,鍵入xxx.h 頭文件所在目錄的路徑或瀏覽至該目錄

若是引用的靜態庫不是在同一解決方案下的子工程,而是使用第三方提供的靜態庫lib和頭文件,上面的方法設置不了。還有2中方法設置均可行。

方法二:

打開工程「屬性面板」→」配置屬性」→ 「連接器」→ 」命令行」,輸入靜態庫的完整路徑便可。

方法三:

  • 「屬性面板」→」配置屬性」→「連接器」→」常規」,附加依賴庫目錄中輸入,靜態庫所在目錄;
  • 「屬性面板」→」配置屬性」→「連接器」→」輸入」,附加依賴庫中輸入靜態庫名StaticLibrary.lib

     

動態庫

經過上面的介紹發現靜態庫,容易使用和理解,也達到了代碼複用的目的,那爲何還須要動態庫呢?

爲何還須要動態庫?

爲何須要動態庫,其實也是靜態庫的特色致使。

  • 空間浪費是靜態庫的一個問題。(靜態庫會嵌入程序中,因此會存在多份拷貝)
  • 另外一個問題是靜態庫對程序的更新、部署和發佈頁會帶來麻煩。若是靜態庫liba.lib更新了,因此使用它的應用程序都須要從新編譯、發佈給用戶(對於玩家來講,多是一個很小的改動,卻致使整個程序從新下載,全量更新)。
  • 動態庫在程序編譯時並不會被鏈接到目標代碼中,而是在程序運行是才被載入。不一樣的應用程序若是調用相同的庫,那麼在內存裏只須要有一份該共享庫的實 例,規避了空間浪費問題。動態庫在程序運行是才被載入,也解決了靜態庫對程序的更新、部署和發佈頁會帶來麻煩。用戶只須要更新動態庫便可,增量更新。

動態庫特色總結:

  •  動態庫把對一些庫函數的連接載入推遲到程序運行的時期。
  •  能夠實現進程之間的資源共享。(所以動態庫也稱爲共享庫)
  •  將一些程序升級變得簡單。
  •  甚至能夠真正作到連接載入徹底由程序員在程序代碼中控制(顯示調用)。

Window與Linux執行文件格式不一樣,在建立動態庫的時候有一些差別。

  •  在Windows系統下的執行文件格式是PE格式,動態庫須要一個DllMain函數作出初始化的入口,一般在導出函數的聲明時須要有_declspec(dllexport)關鍵字。
  •  Linux下gcc編譯的執行文件默認是ELF格式,不須要初始化入口,亦不須要函數作特別的聲明,編寫比較方便。

與建立靜態庫不一樣的是,不須要打包工具(ar、lib.exe),直接使用編譯器便可建立動態庫。

 

Windows下建立與使用動態庫

建立動態庫(.dll)

首先,須要一個DllMain函數作出初始化的入口(建立win32控制檯程序時,勾選DLL類型會自動生成這個文件):

 1 // dllmain.cpp : Defines the entry point for the DLL application.
 2 #include "stdafx.h"
 3  
 4 BOOL APIENTRY DllMain( HMODULE hModule,
 5                        DWORD  ul_reason_for_call,
 6                        LPVOID lpReserved
 7                      )
 8 {
 9     switch (ul_reason_for_call)
10     {
11     case DLL_PROCESS_ATTACH:
12     case DLL_THREAD_ATTACH:
13     case DLL_THREAD_DETACH:
14     case DLL_PROCESS_DETACH:
15         break;
16     }
17     return TRUE;
18 }

 

一般在導出函數的聲明時須要有_declspec(dllexport)關鍵字:

#ifdef XXX_EXPORTS
#define XXX_API __declspec(dllexport)
#else
#define XXX_API __declspec(dllimport)
#endif

 

 

生成動態庫須要設置工程屬性,打開工程「屬性面板」→」配置屬性」→」常規」,配置類型選擇動態庫。

Build項目便可生成動態庫。

使用動態庫

 

方法一:

  • 工程「屬性面板」→「通用屬性」→「框架和引用」→」添加引用」,將顯示「添加引用」對話框。「項目」選項卡列出了當前解決方案中的各個項目以及能夠引用的全部庫。 在「項目」選項卡中,選擇動態庫(工程也是要在項目中?)。 單擊「肯定
  • 添加XXX.h 頭文件目錄,必須修改包含目錄路徑。打開工程「屬性面板」→」配置屬性」→「C/C++」→」 常規」,在「附加包含目錄」屬性值中,鍵入XXX.h 頭文件所在目錄的路徑或瀏覽至該目錄

方法二:

  •  「屬性面板」→」配置屬性」→「連接器」→」常規」,附加依賴庫目錄中輸入,動態庫所在目錄
  • 「屬性面板」→」配置屬性」→「連接器」→」輸入」,附加依賴庫中輸入動態庫編譯出來的XXX.lib

這裏可能你們有個疑問,動態庫怎麼還有一個XXX.lib文件?即不管是靜態連接庫仍是動態連接庫,最後都有lib文件,那麼二者區別是什麼呢?其實,兩個是徹底不同的東西。

靜態庫對應的lib文件叫靜態庫, 動態庫對應的lib文件叫【導入庫】。實際上靜態庫自己就包含了實際執行代碼、符號表等等,而對於導入庫而言,其實際的執行代碼位於動態庫中,導入庫只包 含了地址符號表等,確保程序找到對應函數的一些基本地址信息。

 

動態庫的顯式調用

上面介紹的動態庫使用方法和靜態庫相似屬於隱式調用,編譯的時候指定相應的庫和查找路徑。其實,動態庫還能夠顯式調用。【在C語言中】,顯示調用一個動態庫垂手可得!

在Windows下顯式調用動態庫

應用程序必須進行函數調用以在運行時顯式加載 DLL。爲顯式連接到 DLL,應用程序必須:

  • 調用 LoadLibrary(或類似的函數)以加載 DLL 和獲取模塊句柄。
  • 調用 GetProcAddress,以獲取指向應用程序要調用的每一個導出函數的函數指針。因爲應用程序是經過指針調用 DLL 的函數,編譯器不生成外部引用,故無需與導入庫連接。
  • 使用完 DLL 後調用 FreeLibrary。

顯式調用C++動態庫注意點

對C++來講,狀況稍微複雜。顯式加載一個C++動態庫的困難一部分是由於C++的name mangling;另外一部分是由於沒有提供一個合適的API來裝載類,在C++中,您可能要用到庫中的一個類,而這須要建立該類的一個實例,這不容易作到。

name mangling能夠經過extern 「C」解決。C++有個特定的關鍵字用來聲明採用C binding的函數:extern 「C」 。用 extern 「C」聲明的函數將使用函數名做符號名,就像C函數同樣。所以,只有非成員函數才能被聲明爲extern 「C」,而且不能被重載。儘管限制多多,extern 「C」函數仍是很是有用,由於它們能夠象C函數同樣被dlopen動態加載。冠以extern 「C」限定符後,並不意味着函數中沒法使用C++代碼了,相反,它仍然是一個徹底的C++函數,可使用任何C++特性和各類類型的參數。

 

總結

兩者的不一樣點在於代碼被載入的時刻不一樣。

  •  靜態庫在程序編譯時會被鏈接到目標代碼中,程序運行時將再也不須要該靜態庫,所以體積較大。
  •  動態庫在程序編譯時並不會被鏈接到目標代碼中,而是在程序運行是才被載入,所以在程序運行時還須要動態庫存在,所以代碼體積較小。

動態庫的好處是,不一樣的應用程序若是調用相同的庫,那麼在內存裏只須要有一份該共享庫的實例。帶來好處的同時,也會有問題!如經典的DLL Hell問題,關於如何規避動態庫管理問題,能夠自行查找相關資料。

相關文章
相關標籤/搜索