C++:LIB和DLL的區別與使用

共有兩種庫:編程

  一種是LIB包含了函數所在的DLL文件和文件中函數位置的信息(入口),代碼由運行時加載在進程空間中的DLL提供,稱爲動態連接庫dynamic link library。數據結構

  一種是LIB包含函數代碼自己,在編譯時直接將代碼加入程序當中,稱爲靜態連接庫static link library。編程語言

  共有兩種連接方式:函數

  動態連接使用動態連接庫,容許可執行模塊(.dll文件或.exe文件)僅包含在運行時定位DLL函數的可執行代碼所需的信息。指針

  靜態連接使用靜態連接庫,連接器從靜態連接庫LIB獲取全部被引用函數,並將庫同代碼一塊兒放到可執行文件中。調試

  關於lib和dll的區別以下:索引

  (1)lib是編譯時用到的,dll是運行時用到的。若是要完成源代碼的編譯,只須要lib;若是要使動態連接的程序運行起來,只須要dll。接口

  (2)若是有dll文件,那麼lib通常是一些索引信息,記錄了dll中函數的入口和位置,dll中是函數的具體內容;若是隻有lib文件,那麼這個lib文件是靜態編譯出來的,索引和實現都在其中。使用靜態編譯的lib文件,在運行程序時不須要再掛動態庫,缺點是致使應用程序比較大,並且失去了動態庫的靈活性,發佈新版本時要發佈新的應用程序才行。遊戲

  (3)動態連接的狀況下,有兩個文件:一個是LIB文件,一個是DLL文件。LIB包含被DLL導出的函數名稱和位置,DLL包含實際的函數和數據,應用程序使用LIB文件連接到DLL文件。在應用程序的可執行文件中,存放的不是被調用的函數代碼,而是DLL中相應函數代碼的地址,從而節省了內存資源。DLL和LIB文件必須隨應用程序一塊兒發行,不然應用程序會產生錯誤。若是不想用lib文件或者沒有lib文件,能夠用WIN32 API函數LoadLibrary、GetProcAddress裝載。進程

  使用lib需注意兩個文件:

  (1).h頭文件,包含lib中說明輸出的類或符號原型或數據結構。應用程序調用lib時,須要將該文件包含入應用程序的源文件中。

  (2).LIB文件,略。

  使用dll需注意三個文件:

  (1).h頭文件,包含dll中說明輸出的類或符號原型或數據結構的.h文件。應用程序調用dll時,須要將該文件包含入應用程序的源文件中。

  (2).LIB文件,是dll在編譯、連接成功以後生成的文件,做用是當其餘應用程序調用dll時,須要將該文件引入應用程序,不然產生錯誤。若是不想用lib文件或者沒有lib文件,能夠用WIN32 API函數LoadLibrary、GetProcAddress裝載。

  (3).dll文件,真正的可執行文件,開發成功後的應用程序在發佈時,只須要有.exe文件和.dll文件,並不須要.lib文件和.h頭文件。

  使用lib的方法:

  靜態lib中,一個lib文件其實是任意個obj文件的集合,obj文件是cpp文件編譯生成的。在編譯這種靜態庫工程時,根本不會遇到連接錯誤;即便有錯,也只會在使用這個lib的EXT文件或者DLL工程裏暴露出來。

  在VC中新建一個static library類型的工程Lib,加入test.cpp文件和test.h文件(頭文件內包括函數聲明),而後編譯,就生成了Lib.lib文件。

  別的工程要使用這個lib有兩種方式:

  (1)在project->link->Object/Library Module中加入Lib.lib文件(先查詢工程目錄,再查詢系統Lib目錄);或者在源代碼中加入指令#pragma comment(lib, 「Lib.lib」)。

  (2)將Lib.lib拷入工程所在目錄,或者執行文件生成的目錄,或者系統Lib目錄中。

  (3)加入相應的頭文件test.h。

  使用DLL的方法:

  使用動態連接中的lib,不是obj文件的集合,即裏面不會有實際的實現,它只是提供動態連接到DLL所須要的信息,這種lib能夠在編譯一個DLL工程時由編譯器生成。

  建立DLL工程的方法(略)。

  (1)隱式連接

  第一種方法是:經過project->link->Object/Library Module中加入.lib文件(或者在源代碼中加入指令#pragma comment(lib, 「Lib.lib」)),並將.dll文件置入工程所在目錄,而後添加對應的.h頭文件。

  #include "stdafx.h"

  #include "DLLSample.h"

  #pragma comment(lib, "DLLSample.lib") //你也能夠在項目屬性中設置庫的連接

  int main()

  {

  TestDLL(123); //dll中的函數,在DllSample.h中聲明

  return(1);

  }

  (2)顯式連接

  須要函數指針和WIN32 API函數LoadLibrary、GetProcAddress裝載,使用這種載入方法,不須要.lib文件和.h頭文件,只須要.dll文件便可(將.dll文件置入工程目錄中)。

  #include

  #include //使用函數和某些特殊變量

  typedef void (*DLLFunc)(int);

  int main()

  {

  DLLFunc dllFunc;

  HINSTANCE hInstLibrary = LoadLibrary("DLLSample.dll");

  if (hInstLibrary == NULL)

  {

  FreeLibrary(hInstLibrary);

  }

  dllFunc = (DLLFunc)GetProcAddress(hInstLibrary, "TestDLL");

  if (dllFunc == NULL)

  {

  FreeLibrary(hInstLibrary);

  }

  dllFunc(123);

  std::cin.get();

  FreeLibrary(hInstLibrary);

  return(1);

  }

  LoadLibrary函數利用一個名稱做爲參數,得到DLL的實例(HINSTANCE類型是實例的句柄),一般調用該函數後須要查看一下函數返回是否成功,若是不成功則返回NULL(句柄無效),此時調用函數FreeLibrary釋放DLL得到的內存。

  GetProcAddress函數利用DLL的句柄和函數的名稱做爲參數,返回相應的函數指針,同時必須使用強轉;判斷函數指針是否爲NULL,若是是則調用函數FreeLibrary釋放DLL得到的內存。此後,可使用函數指針來調用實際的函數。

  最後要記得使用FreeLibrary函數釋放內存。

  注意:應用程序如何找到DLL文件?

  使用LoadLibrary顯式連接,那麼在函數的參數中能夠指定DLL文件的完整路徑;若是不指定路徑,或者進行隱式連接,Windows將遵循下面的搜索順序來定位DLL:

  (1)包含EXE文件的目錄

  (2)工程目錄

  (3)Windows系統目錄

  (4)Windows目錄

  (5)列在Path環境變量中的一系列目錄

  .h頭文件是編譯時必須的,lib是連接時須要的,dll是運行時須要的。

  附加依賴項的是.lib不是.dll,若生成了DLL,則確定也生成 LIB文件。若是要完成源代碼的編譯和連接,有頭文件和lib就夠了。若是也使動態鏈接的程序運行起來,有dll就夠了。在開發和調試階段,固然最好都有。

  .h .lib .dll三者的關係是:

  H文件做用是:聲明函數接口

  DLL文件做用是: 函數可執行代碼

  當咱們在本身的程序中引用了一個H文件裏的函數,編鏈器怎麼知道該調用哪一個DLL文件呢?這就是LIB文件的做用: 告訴連接器 調用的函數在哪一個DLL中,函數執行代碼在DLL中的什麼位置 ,這也就是爲何須要附加依賴項 .LIB文件,它起到橋樑的做用。若是生成靜態庫文件,則沒有DLL ,只有lib,這時函數可執行代碼部分也在lib文件中

  目前以lib後綴的庫有兩種,一種爲靜態連接庫(Static Libary,如下簡稱「靜態庫」),另外一種爲動態鏈接庫(DLL,如下簡稱「動態庫」)的導入庫(Import Libary,如下簡稱「導入庫」)。靜態庫是一個或者多個obj文件的打包 ,因此有人乾脆把從obj文件生成lib的過程稱爲Archive,即合併到一塊兒。好比你連接一個靜態庫,若是其中有錯,它會準確的找到是哪一個obj有錯,即靜態lib只是殼子。動態庫通常會有對應的導入庫,方便程序靜態載入動態連接庫 ,不然你可能就須要本身LoadLibary調入DLL文件,而後再手工GetProcAddress得到對應函數了。有了導入庫,你只須要連接導入庫後按照頭文件函數接口的聲明調用函數就能夠了。導入庫和靜態庫的區別很大,他們實質是不同的東西。靜態庫自己就包含了實際執行代碼、符號表等等,而對於導入庫而言,其實際的執行代碼位於動態庫中,導入庫只包含了地址符號表等,確保程序找到對應函數的一些基本地址信息。

  通常的動態庫程序有lib文件和dll文件。lib文件是必須在編譯期就鏈接到應用程序中的,而dll文件是運行期纔會被調用的。 若是有dll文件,那麼對應的lib文件通常是一些索引信息,具體的實如今dll文件中。若是隻有lib文件,那麼這個lib文件是靜態編譯出來的,索引和實現都在其中。靜態編譯的lib文件有好處:給用戶安裝時就不須要再掛動態庫了。但也有缺點,就是致使應用程序比較大,並且失去了動態庫的靈活性,在版本升級時,同時要發佈新的應用程序才行。在動態庫的狀況下,有兩個文件,而一個是引入庫(.LIB)文件,一個是DLL文件,引入庫文件包含被DLL導出的函數的名稱和位置,DLL包含實際的函數和數據,應用程序使用LIB文件連接到所須要使用的DLL文件,庫中的函數和數據並不複製到可執行文件中,所以在應用程序的可執行文件中,存放的不是被調用的函數代碼,而是DLL中所要調用的函數的內存地址,這樣當一個或多個應用程序運行是再把程序代碼和被調用的函數代碼連接起來,從而節省了內存資源。從上面的說明能夠看出,DLL和.LIB文件必須隨應用程序一塊兒發行,不然應用程序將會產生錯誤。

  靜態庫和共享庫都是一個obj文件的集合 ,但靜態連接後,執行程序中存在本身所需obj的一份拷貝,而動態連接後,執行程序僅僅是包含對共享庫的一個引用。共享庫至關於一個由多個obj文件組合而成的obj文件,在連接後其全部代碼被加載,無論須要的仍是不須要的。

  彷佛能夠得出一個結論:

  靜態連接後的程序比動態連接的所用存儲空間大,由於執行程序中包含了庫中代碼拷貝;

  而動態連接的程序比靜態連接的所用的運行空間大,由於它將不須要的代碼也加載到運行空間。

  針對上面的知識2 個問題:

  1) DLL和.LIB文件必須隨應用程序一塊兒發行,不然應用程序將會產生錯誤。

  個人答案:lib應該不須要吧。

  2)若是是某個程序中調用了一個動態庫(經過header文件,lib+dll來調用),則對動態庫的某個函數的內容修改了,但接口不改,則調用此動態庫的程序需從新編譯鏈接嗎?若是是經過loadlibary動態加載,須要從新編譯鏈接嗎?

  個人答案:經過header+lib+dll調用的話須要從新編譯鏈接,可是經過loadlibrary來使用的話,不須要從新編譯鏈接。

  第2個答案錯了 應該是不須要從新編譯 接口不變的話 .lib都不用更新

  1) 節省內存。同一個軟件模塊,如果以源代碼的形式重用,則會被編譯到不一樣的可執行程序中,同時運行這些exe時這些模塊的二進制碼會被重複加載到內存中。如 果使用dll,則只在內存中加載一次,全部使用該dll的進程會共享此塊內存(固然,像dll中的全局變量這種東西是會被每一個進程複製一份的)。

  2) 不需編譯的軟件系統升級,若一個軟件系統使用了dll,則該dll被改變(函數名不變)時,系統升級只須要更換此dll便可,不須要從新編譯整個系統。事實上,不少軟件都是以這種方式升級的。例如咱們常常玩的星際、魔獸等遊戲也是這樣進行版本升級的。

  3) Dll庫能夠供多種編程語言使用,例如用c編寫的dll能夠在vb中調用。這一點上DLL還作得很不夠,所以在dll的基礎上發明了COM技術,更好的解決了一系列問題