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