標籤:
參考:http://blog.jobbole.com/86852/
由於我只在windows下使用,linux部分就不多說了,總結一下windows下面的相關知識好了:
靜態庫
之所以成為【靜態庫】,是因為在連結階段,會將彙編產生的目標檔案.o與引用到的庫一起連結打包到可執行檔中。因此對應的連結方式稱為靜態連結。
試想一下,靜態庫與彙編產生的目標檔案一起連結為可執行檔,那麼靜態庫必定跟.o檔案格式相似。其實一個靜態庫可以簡單看成是一組目標檔案(.o/.obj檔案)的集合,即很多目標檔案經過壓縮打包後形成的一個檔案。靜態庫特點總結:
- 靜態庫對函數庫的連結是放在編譯時間期完成的。
- 程式在運行時與函數庫再無瓜葛,移植方便。
- 浪費空間和資源,因為所有相關的目標檔案與牽涉到的函數庫被連結合成一個可執行檔。
Windows下建立與使用靜態庫
建立靜態庫(.lib)
如果是使用VS命令列產生靜態庫,也是分兩個步驟來產生程式:
- 首先,通過使用帶編譯器選項 /c 的 Cl.exe 編譯代碼 (cl /c xxx.cpp),建立名為“xxx.obj”的目標檔案。
- 然後,使用庫管理器 Lib.exe 連結代碼 (lib xxx.obj),建立靜態庫xxx.lib
當然,我們一般不這麼用,使用VS工程設定更方便。建立win32控制台程式時,勾選靜態庫類型;開啟工程“屬性面板”→”配置屬性”→”常規”,配置類型選擇靜態庫。
Build項目即可產生靜態庫。
使用靜態庫
測試代碼Linux下面的一樣。有3種使用方法:
方法一:
在VS中使用靜態庫方法:
- 工程“屬性面板”→“通用屬性”→“架構和引用”→”添加引用”,將顯示“添加引用”對話方塊。 “項目”選項卡列出了當前解決方案中的各個項目以及可以引用的所有庫。 在“項目”選項卡中,選擇要添加的庫(工程要在解決方案下?)。 單擊“確定
- 添加xxx.h 標頭檔目錄,必須修改包含目錄路徑。開啟工程“屬性面板”→”配置屬性”→“C/C++”→” 常規”,在“附加元件封裝含目錄”屬性值中,鍵入xxx.h 標頭檔所在目錄的路徑或瀏覽至該目錄
如果引用的靜態庫不是在同一解決方案下的子工程,而是使用第三方提供的靜態庫lib和標頭檔,上面的方法設定不了。還有2中方法設定都可行。
方法二:
開啟工程“屬性面板”→”配置屬性”→ “連結器”→ ”命令列”,輸入靜態庫的完整路徑即可。
方法三:
- “屬性面板”→”配置屬性”→“連結器”→”常規”,附加依賴庫目錄中輸入,靜態庫所在目錄;
- “屬性面板”→”配置屬性”→“連結器”→”輸入”,附加依賴庫中輸入靜態庫名StaticLibrary.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問題,關於如何規避動態庫管理問題,可以自行尋找相關資料。
【轉】關於動態庫和靜態庫