溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶服務(wù)條款》

C++ 中的動(dòng)態(tài)庫(kù)和靜態(tài)庫(kù)(Windows)

發(fā)布時(shí)間:2020-07-19 21:37:10 來(lái)源:網(wǎng)絡(luò) 閱讀:17244 作者:shangluyi 欄目:系統(tǒng)運(yùn)維

庫(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)


C++ 中的動(dòng)態(tài)庫(kù)和靜態(tài)庫(kù)(Windows)

C++ 中的動(dòng)態(tài)庫(kù)和靜態(tài)庫(kù)(Windows)

C++ 中的動(dòng)態(tài)庫(kù)和靜態(tài)庫(kù)(Windows)


編譯,得到 *.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


C++ 中的動(dòng)態(tài)庫(kù)和靜態(tài)庫(kù)(Windows)



動(dòng)態(tài)編譯和靜態(tài)編譯的比較:

1、動(dòng)態(tài)編譯不方便發(fā)布,但生成的可執(zhí)行文件體積較小

2、靜態(tài)編譯方便發(fā)布,但生成的可執(zhí)行文件體積較大



向AI問(wèn)一下細(xì)節(jié)

免責(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)容。

AI