1. 程式人生 > >.h .lib .dll檔案作用和區別

.h .lib .dll檔案作用和區別

簡而言之

(1).h是標頭檔案,lib和dll都是庫檔案。

(2)標頭檔案包含宣告,庫檔案則包含實現。

(3)lib是靜態連結時使用的,如果採用靜態連結庫,則lib包含了實際執行程式碼和符號表等,無論你願不願意,lib 中的指令都全部被直接包含在最終生成的 EXE 檔案中了(因此比較耗費記憶體)。

dll用於動態連結,該 dll 不必被包含在最終 EXE 檔案中,EXE 檔案執行時可以“動態”地引用和解除安裝這個與 EXE 獨立的 DLL 檔案。但是生成dll檔案時,需要同時生產lib檔案,此時對應的lib檔案一般是一些索引資訊,具體的實現在dll檔案中。因此在應用程式的可執行檔案中,存放的不是被呼叫的函式程式碼,而是DLL中所要呼叫的函式的記憶體地址,這樣當一個或多個應用程式執行是再把程式程式碼和被呼叫的函式程式碼連結起來,從而節省了記憶體資源。


細而言之

.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檔案必須隨應用程式一起發行,否則應用程式將會產生錯誤。

-------------------------------------------------------------------------------------

靜態連結庫(Lib)與動態連結庫(DLL)的區別 

 靜態連線庫就是把(lib)檔案中用到的函式程式碼直接連結進目標程式,程式執行的時候不再需要其它的庫檔案;動態連結就是把呼叫的函式所在檔案模組(DLL)和呼叫函式在檔案中的位置等資訊連結進目標程式,程式執行的時候再從DLL中尋找相應函式程式碼,因此需要相應DLL檔案的支援。

靜態連結庫與動態連結庫都是共享程式碼的方式,如果採用靜態連結庫,則無論你願不願意,lib 中的指令都全部被直接包含在最終生成的 EXE 檔案中了。但是若使用 DLL,該 DLL 不必被包含在最終 EXE 檔案中,EXE 檔案執行時可以“動態”地引用和解除安裝這個與 EXE 獨立的 DLL 檔案。靜態連結庫和動態連結庫的另外一個區別在於靜態連結庫中不能再包含其他的動態連結庫或者靜態庫,而在動態連結庫中還可以再包含其他的動態或靜態連結庫。


“每一個lib檔案就是若干函式(假設只有函式)的定義” 
lib庫有兩種,一種是包含了函式所在DLL檔案和檔案中函式位置的資訊,稱為匯出庫;一種是包含函式程式碼本身,一般現有的DLL,用的是前一種庫;以前在DOS下的TC/BC等,是後一種庫。包含函式原型宣告的,是標頭檔案(.h)。 

“通過#include包含這些函式宣告的標頭檔案後,我們的應用程式就可以使用lib檔案中的函式”

還要指定編譯器連結相應的庫檔案。在IDE環境下,一般是一次指定所有用到的庫檔案,編譯器自己尋找每個模組需要的庫;在命令列編譯環境下,需要指定每個模組呼叫的庫。 

“那他和直接給出那個函式定義的檔案,比如.cpp檔案,和標頭檔案有什麼區別,靜態連結庫有什麼用” 
cpp檔案是原始碼,庫檔案是編譯後的二進位制程式碼,比如你可以呼叫Windows的API,但是不能看到其原始碼一樣。 

“還有不明白的是,靜態連結庫中的lib檔案只要用到,則整個lib檔案的內容都放進了exe檔案中,那它是被編譯進去還是連結的時候連線進去的呢?” 
是在連結的時候將lib連結到目的碼中。

靜態連結庫(Lib)
在VC++6.0中new一個名稱為libTest的static library工程,

並新建lib.h和lib.cpp兩個檔案,lib.h和lib.cpp的原始碼如下:

//檔案:lib.h
#ifndef LIB_H
#define LIB_H
extern "C" int add(int x,int y);   //宣告為C編譯、連線方式的外部函式
#endif

//檔案:lib.cpp
#include "lib.h"
int add(int x,int y)
{
return x + y;
}

 編譯這個工程就得到了一個.lib檔案,這個檔案就是一個函式庫,它提供了add的功能。將標頭檔案和.lib檔案提交給使用者後,使用者就可以直接使用其中的add函數了。

  標準Turbo C2.0中的C庫函式(我們用來的scanf、printf、memcpy、strcpy等)就來自這種靜態庫。

下面來看看怎麼使用這個庫,在libTest工程所在的工作區內new一個libCall工程。libCall工程僅包含一個main.cpp檔案,它演示了靜態連結庫的呼叫方法,其原始碼如下:

#include <stdio.h>
#include "..\lib.h"//不可丟失
#pragma comment( lib, "..\\debug\\libTest.lib" )  //指定與靜態庫一起連線
int main(int argc, char* argv[])
{
     printf( "2 + 3 = %d", add( 2, 3 ) );
}
  靜態連結庫的呼叫就是這麼簡單,或許我們每天都在用,可是我們沒有明白這個概念。程式碼中#pragma comment( lib , "..\\debug\\libTest.lib" )的意思是指本檔案生成的.obj檔案應與libTest.lib一起連線

-------------------------------------------------------------------------------------------

用VC++生成靜態庫檔案

今天閒著沒事做,自己寫了一點小筆記,不知道對於新手有沒用,高手就不用看了,作為新手的我斗膽來發表一個筆記,就是靜態庫檔案的封裝過程,使用VC++6.0編寫,下面是正文,也許我的用語並不專業

以前我們寫C/C++原始檔的時候,都是先將各個寫好的原始檔編譯,編譯生成的是目標檔案機器碼,即.obj檔案.(目標檔案的副檔名不一定是.obj檔案).

我們呼叫的標準C/C++函式機器碼實際被封裝於標準C/C++靜態庫檔案中的.即那些副檔名為.lib的檔案中.

最後連結器將我們編譯的各個目標檔案裡的機器碼和靜態庫(標準C/C++庫)中的函式機器碼連結到一起形成一個副檔名為.exe的可執行檔案模組.

在這裡我們敘述將C/C++原始檔編譯連結成一個靜態庫檔案,但它不是可執行模組,它體內含有可執行機器碼

靜態庫檔案就像一個倉庫或者容器,裡面封裝了一些可執行機器碼.這些機器碼是我們用程式設計語言,比如C/C++原始檔編譯後生成的機器碼.

.下面將討論將C/C++原始檔編譯並連結成一個靜態庫檔案的過程,

VC++6.0中選擇File-New-Win32 Static Library,寫好工程名建立好工作空間後再選擇選單中New-File來為工程新增C或者C++ 原始檔.

假如我們為該工程添加了一個名為lib_c.c和一個名為lib_cpp.cpp的原始檔

//lib_c.c中的內容

extern int Add(int x,int y) //該函式是一個外部函式,任何檔案都可以訪問它

{
    return x+y;

}

extern int data_c
//
這是一個外部全域性變數,任何檔案可以訪問它

//lib_cpp.cpp中的內容

extern “C” int
        reduce(int x,int y)//
這裡加了個”C”表示允許C原始檔訪問這個C++函式程式碼

{
    return x-y;

}

extern “C” int data_cpp=2;

注意以下幾點

(1)“extern”關鍵字修飾在函式或全域性變數的定義中時,表示該函式或全域性變數任何檔案可以訪問,“extern”關鍵字可以省略不寫,預設下就是”extern”

“extern”關鍵字修飾在函式宣告或全域性變數宣告中時,表示限定當前檔案只能引用用“extern”關鍵字修飾定義的函式或全域性變數.

(2)”static”關鍵字修飾在函式或全域性變數的定義中時,表示該函式或全域性變數只能由本檔案中加了”static”關鍵字修飾的函式宣告或全域性變數宣告來引用.

”static”關鍵字修飾在函式宣告或全域性變數宣告中時,表示限定當前檔案只能引用用“static”關鍵字修飾定義的函式或全域性變數.

(3)CPP原始檔的函式和全域性變數定義中加了個”C”表示允許C原始檔訪問該函式和全域性變數.如果是C++原始檔訪它們的話則可加可不加.注意這”C”要大寫.

接下來就要將寫好的C/C++原始檔進行編譯和連結,最後會生成一個副檔名為.lib的檔案.該檔案就是靜態庫檔案了,該靜態庫檔案是不能直接執行的,我們所編譯的C/C++原始檔的機器碼就已經被封裝進這個用VC++6.0建立的靜態庫檔案裡面去了.

.如何將編寫好的靜態庫檔案像使用C/C++標準庫那樣使用,下面將繼續討論

1.VC++6.0新建一個工程名為TEST,新增一個名為TEST.c的原始檔到該工程,因為我們將測試一下,將我們編寫的庫檔案裡的函式或者全域性變數的機器碼連結到我們這個TEST.c原始檔中去,假設我們生成的庫檔名為TEST.lib,先拷貝如下範例程式碼到TEST.c

//TEST.c

#include <stdio.h>

extern int 
Add(int x,int y); //
當前檔案只能訪問“extern”關鍵字修飾定義的Add函式

extern int
reduce(int x,int y);// //
當前檔案只能訪問“extern”關鍵字修飾定義的reduce函式

#pragma comment(lib,"TEST.lib") //指示連結器到字串所表示的檔案路徑中去找庫檔案

int main()

{
    printf("%d\n",Add(2,3));

    printf("%d\n",reduce(3,2));

    return 0;

}

這裡我們要宣告靜態庫中已知的函式或全域性變數的宣告

#pragma comment(lib,"TEST.lib")這條指令告訴連結器到字串所表示的路徑下去找庫檔案,這裡我將庫檔案放到了當前工程目錄下.也可以不寫這句.

還有一種方法,可以直接在VC++6.0中設定依次選擇toolsoptionsdirectorieslibrary files選單或選項,填入庫檔案路徑(只鍵入庫檔案所在目錄路徑而不能輸入庫檔名),這只是告訴連結器庫檔案所在目錄的路徑,還沒告訴連結器庫檔名,方法是VC++6.0中設定依次選擇project-settings-link object/library modules: 這欄輸入庫檔名字然後就OK

2.當用C++原始檔的目標檔案和庫檔案的程式碼連結時有一點小改變,這裡就不浪費口舌了,假設我們新建了一個工程並添加了一個名為TEST.CPP的原始檔,拷貝如下範例程式碼到TEST.CPP

//TEST.cpp

#include <stdio.h>

extern “C” int 
       Add(int x,int y); //
表示引用的是C函式程式碼

extern int
      reduce(int x,int y);

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

int main()

{
    printf("%d\n",Add(2,3));

    printf("%d\n",reduce(3,2));

    return 0;

}

在這個C++原始檔裡引用C函式程式碼同樣要加個”C”,但是在C原始檔引用C++函式程式碼不能加”C++”,編譯會報錯,只能在C++檔案函式定義中加”C”.

只有C++才支援這種引用方式,也許因為只有C++相容C而沒有C相容C++這一原則.