2011-02-16 74 views
18

鏈接到包含文件與鏈接到lib文件有什麼區別?包括目錄與lib目錄概念問題

我對C/C++相當陌生,我很難弄清楚使用包含文件和靜態lib文件來調用函數的區別。在我看來,include文件具有可以像.lib文件一樣調用的函數。

+1

你知道編譯和鏈接的區別嗎?這可能有助於澄清答案。 – sstn 2011-02-16 21:37:29

回答

28

在C++(和C以及其他類似的語言)的功能被認爲既具有聲明定義

該聲明只是一個簡短的聲明,聲明該函數存在以及它的接口是什麼樣子。考慮將兩個整數加在一起的基本功能add。它的聲明可能看起來像下面這樣:

int add(int, int); 

這意味着「存在一個函數add這需要兩個整數並返回一個整數」。儘管我們可以根據名稱做出一個很好的猜測,但它沒有具體說明函數實際做了什麼。

該函數的定義是我們在哪裏定義的功能確實是。這可能是你認爲是實際的功能代碼。以add功能爲例:

int add (int a, int b) 
{ 
    return a + b; 
} 

那麼這與您的問題如何吻合呢?好吧,假設我們在math.cpp一些數學函數:

// math.cpp 

int add (int a, int b) 
{ 
    return a + b; 
} 

int sub(int a, int b) 
{ 
    return a - b; 
} 

而且還假設我們決定在main.cpp使用其中的一些在我們的主要功能:如果你試圖編譯main.cpp

// main.cpp 

#include <iostream> 

int main (int argc, char* argv[]) 
{ 
    std::cout << "1 + 2 = " << add(1, 2) << std::endl; 
    std::cout << "8 - 3 = " << sub(8, 3) << std::endl; 
} 

事實上,它會抱怨說它不知道什麼是addsub。這是因爲你試圖在不聲明它們存在的情況下使用它們 - 這正是聲明的目的。所以你可能做到以下幾點:

// main.cpp 

#include <iostream> 

int add(int, int); 
int sub(int, int); 

int main (int argc, char* argv[]) 
{ 
    std::cout << "1 + 2 = " << add(1, 2) << std::endl; 
    std::cout << "8 - 3 = " << sub(8, 3) << std::endl; 
} 

這會工作,但不是很靈活。如果我們添加一個新函數mul,我們需要去使用它的聲明並將其聲明添加到main.cpp以及使用它的每個其他.cpp文件(如果您有很多.cpp文件,這是很多工作)。所以我們所做的是將所有的聲明放到一個文件中(比如說,math.h),所以我們只需要在一個地方保存聲明列表。然後,我們只需將math.h包含到使用數學函數的任何文件中。這是頭文件(又名包含文件)的目的。

這很好,但可能會更好。實際上,我們有一個main.cpp文件和一個math.cpp文件,這兩個文件都是每次編譯程序*時編譯的。如果你的數學函數根本沒有改變,那麼最好編譯一次,只要你重新編譯main.cpp就插入預編譯的定義到你的可執行文件中?這正是.lib文件的用途。它們包含相關函數的定義的預編譯代碼。你仍然需要include文件來讓你知道lib中存在哪些函數。

編譯鏈接階段的目的是將這些預編譯函數和剛纔編譯的函數合併成一個可執行文件。從本質上講,你可以看一個靜態lib作爲一些預定義函數的預編譯代碼,它的匹配包含文件作爲一個工具,讓任何代碼想要使用這些函數知道哪些是可用的以及它們是什麼描述是。


*這不完全正確,但足以滿足我們的目的。

+1

感謝您的詳細解釋。這是否意味着在創建庫時,我們總是需要有一個.h文件來引用所有的函數?例如,如果我使用一堆.cpp文件和一個.h文件創建myLibrary.lib來聲明我的函數,那麼我必須將.h文件包含在我使用我的庫引用庫的任何程序中以及鏈接到myLibrary.lib? – foboi1122 2011-02-17 17:45:52

2

包含文件通常包含符號聲明(函數,變量)。這讓我們的編譯器知道一個名字被定義(在頭)或其他地方(在申報的情況下):

a.h: 

void a_useful_function(); //declaration 

,但你也可以有一個定義:

a.h: 

void a_useful_function() 
{ 
    //... do something 
} 

庫是通常由標題暴露的函數的累積。標題通常是您要鏈接的庫的接口。

但是,存在只有頭文件的庫,它們的聲明和定義代碼在相同的文件中。

你提到在你的問題包括目錄。 include目錄是編譯器搜索解決#include「a.h」預處理器指令的地方。

但也有庫目錄,其中鏈接器搜索通常爲頭中的聲明提供實現(定義)所需的庫。

0

提供一種更簡單的答案:

.lib文件是預編譯庫。如果包含.lib,則還需要包含.h/hpp頭文件,以便您的編譯器知道如何訪問.lib中的函數。

當你編譯你的程序時,從lib中使用的所有函數都只能被鏈接,它們會被重新編譯。