您好,登錄后才能下訂單哦!
庫(kù):
在C/C++中,使用庫(kù)(Library)的技術(shù),可以將編譯好的符號(hào)提供給第三方使用。
庫(kù)有兩種:
1、動(dòng)態(tài)庫(kù) Dynamic-Link Library (DLL) (Linux下叫做 Shared Library)
2、靜態(tài)庫(kù) Static Library
一、動(dòng)態(tài)庫(kù)的創(chuàng)建和使用
創(chuàng)建DLL:
用VC創(chuàng)建一個(gè)類型為 “dll”的控制臺(tái)項(xiàng)目,VC會(huì)自動(dòng)創(chuàng)建DLL的項(xiàng)目框架
它自動(dòng)生成一個(gè)DllMain函數(shù),可以類比普通應(yīng)用程序中的main函數(shù)
VC項(xiàng)目設(shè)置:
1、取消“預(yù)編譯頭文件”
2、改為 “/MTd編譯”
3、修改輸出的DLL的名字 (my.dll)
編譯,得到 *.lib 和 *.dll,其中:
*.dll:
包含所有代碼編譯成的指令
*.lib:包含一個(gè)列表,表名my.dll中含有哪些符號(hào),每個(gè)符號(hào)對(duì)應(yīng)在dll中的位置。(被導(dǎo)出的符號(hào))
所以,*.lib比*.dll的文件體積小得多
如果想導(dǎo)出一個(gè)全局函數(shù),就用關(guān)鍵字 __declspec(dllexport)來(lái)聲明
注意:這是VC平臺(tái)特有的關(guān)鍵字,在linux平臺(tái)下不可用
使用如下:
template <typename T> __declspec(dllexport) void MySwap(T& obj1, T& obj2) { T tmp = obj1; obj1 = obj2; obj2 = tml; }
使用dll:
#pragma comment(lib, "12_18_DLL01") __declspec(dllimport) int Add(int a, int b); int main() { int ret = Add(1, 2); std::cout << ret << std::endl; return 0; }
但這個(gè)地方我發(fā)現(xiàn)了一個(gè)問(wèn)題:
__declspec(dllimport) int Add(int a, int b);
這行代碼表示導(dǎo)入dll文件中的符號(hào)
但一開(kāi)始我錯(cuò)誤得寫成了 export, 結(jié)果居然仍然是正確的
沒(méi)有深究,初步推測(cè)是編譯器優(yōu)化了 (IDE: VS2015),甚至不寫前面的關(guān)鍵字,直接寫成函數(shù)的聲明,程序也是能正確執(zhí)行的
DLL的部署位置:
1、可執(zhí)行文件所在目錄
2、進(jìn)程當(dāng)前目錄
3、系統(tǒng)目錄 (C:\Windows\System32\ 和 C:\Windows\System\)
4、Windows目錄 (C:\Windows\)
5、環(huán)境變量 PATH 中的目錄
通過(guò)初步接觸DLL的發(fā)布和使用,相比普通的聲明、定義,會(huì)發(fā)現(xiàn)DLL有以下作用:
1、隱藏了代碼
2、公開(kāi)了功能
當(dāng)然,DLL的作用遠(yuǎn)不止如此
二、DLL的加載和卸載
DLL的加載:
DLL不能獨(dú)立運(yùn)行,只有當(dāng) *.exe 被運(yùn)行時(shí), *.dll 才會(huì)被加載運(yùn)行。
在exe文件中有一些標(biāo)識(shí)信息,表示該 exe 依賴哪些 dll 文件,操作系統(tǒng)據(jù)此去尋找、加載相應(yīng)的dll文件。
exe 被加載到內(nèi)存,形成一個(gè)進(jìn)程 process,dll也被加載到內(nèi)存,進(jìn)程可以直接調(diào)用DLL中的函數(shù)(Code)
數(shù)據(jù)段和代碼段:
在dll文件中,至少分為兩個(gè)段:
1、代碼段:
存儲(chǔ)指令(函數(shù)體)
2、數(shù)據(jù)段:
數(shù)據(jù)段,存放全局變量
當(dāng) *.dll 被加載時(shí),代碼段只被加載一次,是公共的。
數(shù)據(jù)段被每個(gè)程序各自拷貝一份,是私有的
三、DLL中的動(dòng)態(tài)內(nèi)存管理
在dll中申請(qǐng)的動(dòng)態(tài)內(nèi)存,必須在dll中釋放,否則會(huì)導(dǎo)致內(nèi)存泄漏 (這是由Windows自己的特點(diǎn)決定的)
四、DLL中使用頭文件
按照慣例,由模塊的作者提供頭文件,頭文件中應(yīng)該用類型、函數(shù)聲明。
所以,我們?cè)趧?chuàng)建DLL時(shí),應(yīng)該附帶一份頭文件,而不是讓使用者自己去寫函數(shù)聲明。
五、DLL中導(dǎo)出一個(gè)類
導(dǎo)出類的定義,其實(shí)就是導(dǎo)出其成員函數(shù)
類的聲明格式:
class __declspec(dllimport) MyClass {};
六、靜態(tài)庫(kù)的概念及使用
靜態(tài)庫(kù):
static library,僅一個(gè) *.lib 文件
靜態(tài)庫(kù)中直接就含有代碼段和數(shù)據(jù)段,在鏈接過(guò)程中,是直接把里面的東西鏈接過(guò)來(lái),形成完整的可執(zhí)行程序
exe運(yùn)行的時(shí)候不依賴 .lib 文件
創(chuàng)建:
1、建立一個(gè)新工程,工程類型為:靜態(tài)庫(kù)
2、不需要其他設(shè)置,直接編譯鏈接生成,得到 *.lib文件
使用:
#pragma comment(lib, "MyLib.lib") int MyAdd(int a, int b); //通常發(fā)布會(huì)把把聲明放到頭文件中,一并發(fā)布
靜態(tài)庫(kù)的注意事項(xiàng):
1、靜態(tài)庫(kù)的使用不太方便:
如果該靜態(tài)庫(kù)是VS2008編譯的,那么APP也得用VS2008編譯,版本必須一致
此外,運(yùn)行時(shí)庫(kù)(/MT、/MTd、/MD、/MDd)也必須要一致
因此可以看出,靜態(tài)庫(kù)的使用,約束條件是比較多的
靜態(tài)庫(kù)和動(dòng)態(tài)庫(kù)的對(duì)比:
靜態(tài)庫(kù)優(yōu)點(diǎn):
使用靜態(tài)庫(kù),最后得到的可執(zhí)行程序執(zhí)行時(shí)對(duì)這個(gè)庫(kù)不再依賴
動(dòng)態(tài)庫(kù)優(yōu)點(diǎn):
便于升級(jí)更新,只要保持接口不變,可以通過(guò)更新DLL來(lái)升級(jí)程序,而不需要重新編譯程序
目前通常都使用動(dòng)態(tài)庫(kù)
但動(dòng)態(tài)庫(kù)也存在更多的安全方面的隱患,畢竟如果有人惡意替換DLL(能做到這一步的人往往是軟件團(tuán)隊(duì)的內(nèi)部人員),程序的執(zhí)行將會(huì)違背編程者的本意.......
七、DLL的手動(dòng)加載
之前加載DLL的方式是自動(dòng)加載。
自動(dòng)加載和手動(dòng)加載:
1、自動(dòng)加載
在編譯時(shí)指定dll,則當(dāng)exe程序啟動(dòng)運(yùn)行時(shí),首先加載相關(guān)的dll
(DLL在程序執(zhí)行前被加載,在程序結(jié)束后被卸載)
2、手動(dòng)加載
在編譯時(shí)不指定dll,在運(yùn)行時(shí)調(diào)用LoadLibrary來(lái)加載dll
(這樣做,可以自己決定什么時(shí)候加載、什么時(shí)候卸載DLL)
手動(dòng)加載的方式:
#include <winsock2.h> #include <windows.h>
使用 LoadLibrary 來(lái)加載dll,
使用 FreeLibrary 來(lái)卸載dll,
它提供了一種在運(yùn)行時(shí)手動(dòng)加載dll的技術(shù)手段,增加了編程的靈活性
(只要有*.dll就可以,也不需要 *.lib 和 *.h )
對(duì)DLL的要求:
1、要求待調(diào)用的函數(shù)按“C”方式編譯(符號(hào)名即函數(shù)名)
extern "C" __declspec(dllexprot) int MyAdd(int a, int b);
2、dll文件放在可被系統(tǒng)搜索到的路徑
代碼:
#include <iostream> #include <winsock2.h> #include <windows.h> int main() { wchar_t* Path = L"12_18_DLL01.dll"; HINSTANCE handle = LoadLibrary(Path); // 字符集兼容問(wèn)題 if (handle) { // 定義要找的函數(shù)原型 typedef int(*DLL_FUNCTION_ADD) (int, int); // 找到目標(biāo)函數(shù)的地址 注意實(shí)現(xiàn)必須用 extern "C" 的方式編譯 DLL_FUNCTION_ADD dll_func = (DLL_FUNCTION_ADD)GetProcAddress( handle, "Add"); if (dll_func) { // 調(diào)用該函數(shù) int result = dll_func(1, 2); std::cout << result << std::endl; } } FreeLibrary(handle); return 0; }
八、項(xiàng)目的靜態(tài)編譯
有一種場(chǎng)景,將以往VS生成的 *.exe 程序拷貝到其他 沒(méi)有VS運(yùn)行環(huán)境的機(jī)器上,這時(shí)候程序往往是無(wú)法運(yùn)行的,這是因?yàn)?,VC編譯默認(rèn)采用動(dòng)態(tài)編譯的方式,因此 要么給這個(gè)機(jī)器也安裝一套運(yùn)行環(huán)境,要么采用靜態(tài)編譯的方式生成可執(zhí)行文件(*.exe)
靜態(tài)編譯后的程序,將會(huì)包含一切程序運(yùn)行時(shí)所依賴的環(huán)境
對(duì)VC來(lái)說(shuō):
靜態(tài)編譯:
/MT /MTd
動(dòng)態(tài)編譯:
/MD /MDd
動(dòng)態(tài)編譯和靜態(tài)編譯的比較:
1、動(dòng)態(tài)編譯不方便發(fā)布,但生成的可執(zhí)行文件體積較小
2、靜態(tài)編譯方便發(fā)布,但生成的可執(zhí)行文件體積較大
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。