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 <iostream>

  #include <windows.h>         //使用函數和某些特殊變量

  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技術,更好的解決了一系列問題。


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章