lib和dll檔案的區別和聯絡

2021-05-26 19:13:08 字數 4930 閱讀 8879

(1)lib是編譯時需要的,dll是執行時需要的。

如果要完成源**的編譯,有lib就夠了。

如果也使動態連線的程式執行起來,有dll就夠了。

在開發和除錯階段,當然最好都有。

(2)一般的動態庫程式有lib檔案和dll檔案。lib檔案是必須在編譯期就連線到應用程式中的,而dll檔案是執行期才會被呼叫的。如果有dll檔案,那麼對應的lib檔案一般是一些索引資訊,具體的實現在dll檔案中。如果只有lib檔案,那麼這個lib檔案是靜態編譯出來的,索引和實現都在其中。靜態編譯的lib檔案有好處:給使用者安裝時就不需要再掛動態庫了。但也有缺點,就是導致應用程式比較大,而且失去了動態庫的靈活性,在版本公升級時,同時要發布新的應用程式才行。

(3)在動態庫的情況下,有兩個檔案,乙個是引入庫(.lib)檔案,乙個是dll檔案,引入庫檔案包含被dll匯出的函式的名稱和位置,dll包含實際的函式和資料,應用程式使用lib檔案鏈結到所需要使用的dll檔案,庫中的函式和資料並不複製到可執行檔案中,因此在應用程式的可執行檔案中,存放的不是被呼叫的函式**,而是dll中所要呼叫的函式的記憶體位址,這樣當乙個或多個應用程式執行是再把程式**和被呼叫的函式**鏈結起來,從而節省了記憶體資源。從上面的說明可以看出,dll和.lib檔案必須隨應用程式一起發行,否則應用程式將會產生錯誤。

一、開發和使用dll需注意三種檔案    

1、   dll標頭檔案    

它是指dll中說明輸出的類或符號原型或資料結構的.h檔案。當其它應用程式呼叫dll時,需要將該檔案包含入應用程式的原始檔中。    

2、   dll的引入庫檔案    

它是dll在編譯、鏈結成功後生成的檔案。主要作用是當其它應用程式呼叫dll時,需要將該檔案引入應用程式。否則,dll無法引入。    

3、   dll檔案(.dll)    

它是應用程式呼叫dll執行時,真正的可執行檔案。dll應用在編譯、鏈結成功後,.dll檔案即存在。開發成功後的應用程式在發布時,只需要有.exe檔案和.dll檔案,不必有.lib檔案和dll標頭檔案。   

動態鏈結庫 (dll) 是作為共享函式庫的可執行檔案。動態鏈結提供了一種方法,使程序可以呼叫不屬於其可執行**的函式。函式的可執行**位於乙個 dll 中,該 dll 包含乙個或多個已被編譯、鏈結並與使用它們的程序分開儲存的函式。dll 還有助於共享資料和資源。多個應用程式可同時訪問記憶體中單個 dll 副本的內容。 

動態鏈結與靜態鏈結的不同之處在於:動態鏈結允許可執行模組(.dll 檔案或 .exe 檔案)僅包含在執行時定位 dll 函式的可執行**所需的資訊。在靜態鏈結中,鏈結器從靜態鏈結庫獲取所有被引用的函式,並將庫同**一起放到可執行檔案中。 

使用動態鏈結代替靜態鏈結有若干優點。dll 節省記憶體,減少交換操作,節省磁碟空間,更易於公升級,提供售後支援,提供擴充套件 mfc 庫類的機制,支援多語言程式,並使國際版本的建立輕鬆完成。 

printf("hello world!\n");

} int main()

同樣的道理,乙個原始檔中包含了太多的函式,同樣不好理解,人們開始分多個原始檔了

// main.cpp

void showhello();//[1]

int main()

// hello.cpp

#include 

void showhello()

將這兩個檔案加入到乙個vc工程中,它們會被分別編譯,最後鏈結在一起。在vc編譯器的輸出視窗,你可以看到如下資訊

--------------------configuration: hello - win32 debug--------------------

compiling...

main.cpp

hello.cpp

linking... 

hello.exe - 0 error(s), 0 warning(s)

這展示了它們的編譯鏈結過程。

接下來,大家就算不知道也該猜到,當乙個工程中有太多的原始檔時,它也不好理解,於是,人們想到了一種手段:將一部分原始檔預先編譯成庫檔案,也即lib檔案,當要使用其中的函式時,只需要鏈結lib檔案就可以了,而不用再理會最初的原始檔。

在vc中新建乙個static library型別的工程,加入hello.cpp檔案,然後編譯,就生成了lib檔案,假設檔名為hello.lib。

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

1 在工程選項-〉link-〉object/library module中加入hello.lib

2 可以在源**中加入一行指令

#pragma comment(lib, "hello.lib")

注意這個不是c++語言的一部分,而是編譯器的預處理指令,用於通知編譯器需要鏈結hello.lib

根據個人愛好任意使用一種方式既可。

這種lib檔案的格式可以簡單的介紹一下,它實際上是任意個obj檔案的集合。obj檔案則是cpp檔案編譯生成的,在本例中,lib檔案只包含了乙個obj檔案,如果有多個cpp檔案則會編譯生成多個obj檔案,從而生成的lib檔案中也包含了多個obj,注意,這裡僅僅是集合而已,不涉及到link,所以,在編譯這種靜態庫工程時,你根本不會遇到鏈結錯誤。即使有錯,錯誤也只會在使用這個lib的exe或者dll工程中暴露出來。

關於靜態lib,就只有這麼多內容了,真的很簡單,現在我們介紹另外一種型別的lib,它不是obj檔案的集合,即裡面不含有實際的實現,它只是提供動態鏈結到dll所需要的資訊。這種lib可以在編譯乙個dll工程時由編譯器生成。涉及到dll,問題開始複雜起來,我不指望在本文中能把dll的原理說清楚,這不是本文的目標,我介紹操作層面的東西。

簡單的說,乙個dll工程和乙個exe工程的差別有兩點:

1 exe的入口函式是main或者winmain,而dll的入口函式是dllmain

2 exe的入口函式標誌著一段處理流程的開始,函式退出後,流程處理就結束了,而dll的入口函式對系統來說,只是路過,載入dll的時候路過一次,解除安裝dll的時候又路過一次[2],你可以在dll入口函式中做流程處理,但這通常不是dll的目的,dll的目的是要匯出函式供其它dll或exe使用。你可以把dll和exe的關係理解成前面的main.cpp和hello.cpp的關係,有類似,實現手段不同罷了。

先看如何寫乙個dll以及如何匯出函式,讀者應該先嘗試用vc建立乙個新的動態鏈結庫工程,建立時選項不選空工程就可以了,這樣你能得到乙個示例,以便開始在這個例子基礎上工作。

看看你建立的例子中的標頭檔案有類似這樣的語句:

#ifdef dll_exports

#define dll_api __declspec(dllexport)

#else

#define dll_api __declspec(dllimport)

#endif

這就是函式的匯出與使用匯出函式的全部奧妙了。你的dll工程已經在工程設定中定義了乙個巨集dll_exports,因此你的函式宣告只要前面加dll_api就表示把它匯出,而dll的使用者由於沒有定義這個巨集,所以它包含這個標頭檔案時把你的函式看作匯入的。通過模仿這個例子,你就可以寫一系列的標記為匯出的函式了。

匯出函式還有另一種方法,是使用def檔案,def檔案的作用,在現在來說只是起到限定匯出函式名字的作用,這裡,我們要引出第二種[4]使用dll的方法:稱為顯示載入,通過windows api的loadlibrary和getprocaddress這兩個函式來實現[5],這裡getprocaddress的引數需要乙個字串形式的函式名稱,如果dll工程中沒有使用def檔案,那麼很可能你要使用非常奇怪的函式名稱(形如:?fndll@@yahxz)才能正確呼叫,這是因為c++中的函式過載機制把函式名字重新編碼了,如果使用def檔案,你可以顯式指定沒編碼前的函式名。

有了這些知識,你可以開始寫一些簡單的dll的應用,但是我可以百分之百的肯定,你會遇到崩潰,而之前的非dll的版本則沒有問題。假如你通過顯式載入來使用dll,有可能會是呼叫約定不一致而引起崩潰,所謂呼叫約定就是函式宣告前面加上__stdcall __cdecl等等限定詞,注意一些巨集如winapi會定義成這些限定詞之一,不理解他們沒關係,但是記住一定要保持一致,即宣告和定義時一致,這在用隱式載入時不成問題,但是顯示載入由於沒有利用標頭檔案,就有可能產生不一致。

呼叫約定並不是我真正要說的,雖然它是一種可能。我要說的是記憶體分配與釋放的問題。請看下面**:

void foo(string& str)

int main()

當函式foo和main在同乙個工程中,或者foo在靜態庫中時,不會有問題,但是如果foo是乙個dll的匯出函式時,請不要這麼寫,它有可能會導致崩潰[6]。崩潰的原因在於「乙個模組中分配的內存在另乙個模組中釋放」,dll與exe分屬兩個模組,例子中foo裡面賦值操作導致了記憶體分配,而main中return語句之後,string物件析構引起記憶體釋放。

我不想窮舉全部的這類情況,只請大家在設計dll介面時考慮清楚記憶體的分配釋放問題,請遵循誰分配,誰釋放的原則來進行。

如果不知道該怎麼設計,請抄襲我們常見的dll介面--微軟的api的做法,如:

createdc

releasedc

的成對呼叫,乙個函式分配了記憶體,另外乙個函式用來釋放記憶體。

回到我們有可能崩潰的例子中來,怎麼修改才能避免呢?

這可以做為乙個練習讓讀者來做,這個練習用的時間也許會比較長,如果你做好了,那麼你差不多就出師了。一時想不到也不用急,我至少見過兩個有五年以上經驗的程式設計師依然犯這樣的錯誤。

注[1]:為了說明的需要,我這裡使用直接宣告的方式,實際工程中是應該使用標頭檔案的。

注[2]: 還有執行緒建立與銷毀也會路過dll的入口,但是這對新手來說意義不大。

注[3]:def檔案格式很簡單,關於def檔案的例子,可以通過新建乙個atl com工程看到。

注[4]:第一種方法和使用靜態庫差不多,包含標頭檔案,鏈結庫檔案,然後就像是使用普通函式一樣,稱為隱式載入。

注[5]:具體呼叫方法請參閱msdn。

注[6]:之所以說有可能是因為,如果兩個工程的設定都是採用動態連線到執行庫,那麼分配釋放其實都在執行庫的dll中進行,那麼這種情況便不會發生崩潰

lib和dll檔案的區別和聯絡

lib和dll lib和dll檔案的區別和聯絡 dll是在你的程式執行的時候才連線的檔案,因此它是一種比較小的可執行檔案格式,dll還有其他的檔案格式如.ocx等,所有的.dll檔案都是可執行。lib是在你的程式編譯連線的時候就連線的檔案,因此你必須告知編譯器連線的lib檔案在那裡。一般來說,與動態...

lib和dll檔案的區別和聯絡

lib和dll lib和dll檔案的區別和聯絡 dll是在你的程式執行的時候才連線的檔案,因此它是一種比較小的可執行檔案格式,dll還有其他的檔案格式如.ocx等,所有的.dll檔案都是可執行。lib是在你的程式編譯連線的時候就連線的檔案,因此你必須告知編譯器連線的lib檔案在那裡。一般來說,與動態...

lib和dll檔案的區別和聯絡

lib和dll檔案的區別和聯絡 2006 09 23 21 09 lib和dll lib和dll檔案的區別和聯絡 dll是在你的程式執行的時候才連線的檔案,因此它是一種比較小的可執行檔案格式,dll還有其他的檔案格式如.ocx等,所有的.dll檔案都是可執行。lib是在你的程式編譯連線的時候就連線的...