標頭檔案與extern

2022-07-29 17:54:09 字數 4466 閱讀 7817

好吧,標頭檔案是幹嘛的?

放心,這個簡單的問題確實把我難住了。用了多少年頭檔案,標頭檔案是幹嘛用的?

第一,標頭檔案是給別人看得。

第二,標頭檔案可以用來進行型別檢查,減少出錯。

好吧,第二點我似懂非懂,所以只說第一點。再配合extern。他們倆在一起很萬惡的。真的哦!

標頭檔案是給別人看的。不管是c還是c++,你把你的函式,變數或者結構體,類啥的放在你的.c或者.cpp檔案裡。

然後編譯成lib,dll,obj,.o等等,然後別人用的時候 最基本的gcc hisfile.cpp yourfile.o|obj|dll|lib 等等。

ok,他們怎麼知道你的lib,dll...裡面到底有什麼東西?要看你的標頭檔案。你的標頭檔案就是對使用者的說明。函式,引數,各種各樣的介面的說明。

so,既然是說明,那麼標頭檔案裡面放的自然就是關於函式,變數,類的「宣告」了。記著,是「宣告」,不是「定義」。

那麼,我假設大家知道宣告和定義的區別。所以,最好不要傻嘻嘻的在標頭檔案裡定義什麼東西。比如全域性變數:

#ifndef _xx_標頭檔案.h

#define _xx_標頭檔案.h

int a;

#endif

那麼,很糟糕的是,這裡的int a是個全域性變數的定義,所以如果這個標頭檔案被多次引用的話,你的a會被重複定義

顯然語法上錯了。只不過有了這個#ifndef的條件編譯,所以能保證你的標頭檔案只被引用一次,不過也許還是會岔子,所以,最好不要在標頭檔案裡定義太多東西。

那麼 ok

extern int a;//宣告乙個全域性變數a

int a; //定義乙個全域性變數a

extern int a =0 ;//定義乙個全域性變數a 並給初值。

int a =0;//定義乙個全域性變數a,並給初值,

第四個 等於 第 三個,都是定義乙個可以被外部使用的全域性變數,並給初值。

糊塗了吧,他們看上去可真像。但是定義只能出現在一處。也就是說,不管是int a;還是extern int a=0;還是int a=0;都只能出現一次,而那個extern int a可以出現很多次。當你要引用乙個全域性變數的時候,你就要宣告,extern int a;這時候extern不能省略,因為省略了,就變成int a;這是乙個定義,不是宣告。

函式,函式,對於函式也一樣,也是定義和宣告,定義的時候用extern,說明這個函式是可以被外部引用的,宣告的時候用extern說明這是乙個宣告。

但是對於函式extern,不管是宣告還是定義都是可以省略的,因為定義函式要有函式體,宣告函式沒有函式體,兩者如此不同,所以省略了extern也不會有問題。

比如:int fun(void)

很好,我們定義了乙個全域性函式

int fun(void);

我們對它做了個宣告,然後後面就可以用了

加不加extern都一樣

我們也可以把對fun的宣告 放在乙個標頭檔案裡,最後變成這樣

int fun(void);//函式宣告,所以省略了extern,完整些是extern int fun(void);

int fun(void)

//乙個完整的全域性函式定義,因為有函式體,extern同樣被省略了。

然後,乙個客戶,乙個要使用你的fun的客戶,把這個標頭檔案包含進去,ok,乙個全域性的宣告。沒有問題。

但是,對應的,如果是這個客戶要使用全域性變數,那麼要extern 某某變數;不然就成了定義了。

很多情況下,程式設計師可能突然產生這樣的問題:為什麼非得在檔案的首位置#include "... .h"? 我include原始檔行不行。

#ifndef __single__cpp_

#define __single__cpp_

#include

void fun()

#endif/*__single__cpp_*/

#include"

single.cpp

"void main()

vc2008下編譯,出現如下問題:

為什麼會出現這樣的情況呢?

其實c語言的編譯方式是分離式的,分為兩步(簡單起見我們假設只有乙個生成目標):

1、將乙個或多個原始檔編譯成可重定位的目標檔案,其中每個檔案是分別編譯的。

2、將1步生成的可重定位目標檔案鏈結成乙個可執行目標檔案或者共享目標檔案。

因此,假設foo1.c中用include指令包含了foo2.c,foo2.c中定義了函式function且沒有用static修飾。那麼經過預處

理,foo1.c中也將包含function的定義。於是foo1.c生成的可重定位目標檔案(假設為foo1.o)和foo2.c(假設為

foo2.o)都有function這個符號,這樣上面所說的第二步就會因為衝突而失敗。

事實上,在沒有標頭檔案可被包含的情況下(事實上初期,是最早最早的時候),我們可以採用如下的方式:

extern fun();

void main()

程式編譯執行,是我們想要的結果。其實這個就是乙個前置宣告。事實上,我們不加"extern"關鍵字,編譯也正常:

void fun();

void main()

這是因為:函式具有定義和宣告,定義的時候用extern,說明這個函式是可以被外部引用的,宣告的時候用extern說明這是乙個宣告。但由於函

數的定義和宣告是有區別的,定義函式要有函式體,宣告函式沒有函式體,所以函式定義和宣告時都可以將extern省略掉,反正其他檔案也是知道這個函式是

在其他地方定義的,所以不加extern也行。兩者如此不同,所以省略了extern也不會有問題。

我們再看變數的宣告和定義:

extern

int a; //

宣告乙個全域性變數a

int a; //

定義乙個全域性變數a

extern

int a =0 ; //

定義乙個全域性變數a 並給初值。

int a =0; //

定義乙個全域性變數a,並給初值,

第四個等於第三個,都是定義乙個可以被外部使用的全域性變數,並給初值。但是定義只能出現在一處。也就是說,不管是int a;還是extern int a=0;還是int a=0;都只能出現一次,而那個extern int a可以出現很多次。當你要引用乙個全域性變數的時候,你就要宣告,extern int a;這時候extern不能省略,因為省略了,就變成int a;這是乙個定義,不是宣告。

因此,訪問者模組時仍要對變數進行宣告,而這時候是必須要加"extern"關鍵字的。

這樣我們出現乙個問題?既然extern+前置宣告都能實現這些功能?為什麼還得將宣告放在標頭檔案中並被包含呢?我覺得下面這段話說得很清楚:

在c語言家族程式中,標頭檔案被大量使用。一般而言,每個c++/c程式通常由標頭檔案(header files)和定義檔案(definition

files)組成。標頭檔案作為一種包含功能函式、資料介面宣告的載體檔案,用於儲存程式的宣告(declaration),而定義檔案用於儲存程式的實現

(implementation)。

c++/c程式的標頭檔案以「.h」為字尾。以下是假設名稱為 graphics.h的標頭檔案:

#ifndef graphics_h (作用:防止graphics.h被重複引用)

#define graphics_h

#include.... (作用:引用標準庫的標頭檔案)

...#include... (作用:引用非標準庫的標頭檔案)

...void function1(...); (作用:全域性函式宣告)

...class box (作用:類結構宣告)

;#endif

從以上例子可以看出,標頭檔案一般由三部分內容組成:(1)標頭檔案開頭處的版權和版本宣告;(2)預處理塊;(3)函式和類結構宣告等。在標頭檔案中,用

ifndef/define/endif結構產生預處理塊,用 #include

格式來引用庫的標頭檔案。標頭檔案的這種結構,是利用c語言進行開發軟體所通常具備的,屬於公有知識。

一般在乙個應用開發體系中,功能的真正邏輯

實現是以硬體層為基礎,在驅動程式、功能層程式以及使用者的應用程式中完成的。根據以上示例,可以發現標頭檔案的主要作用在於呼叫庫功能,對各個被呼叫函式給

出乙個描述,其本身不包含程式的邏輯實現**,它只起描述性作用,告訴應用程式通過相應途徑尋找相應功能函式的真正邏輯實現**。使用者程式只需要按照頭文

件中的介面宣告來呼叫庫功能,編譯器會從庫中提取相應的**。

從以上結構圖來看,標頭檔案是使用者應用程式和函式庫之間的橋梁和紐帶。在整個軟體

中,標頭檔案不是最重要的部分,但它是c語言家族中不可缺少的組成部分。做乙個不算很恰當的比喻,標頭檔案就像是一本書中的目錄,讀者(使用者程式)通過目錄,

可以很方便就查閱其需要的內容(函式庫)。在一本書中,目錄固然重要,但絕對不是一本書的核心的、最重要的部分。

你既然寫出了乙個模組並且要提供給其它模組使用的功能,你總不能讓其它模組去「猜」你模組的哪些介面吧。所以這時候乾脆再帶個索引一樣的標頭檔案。當然如果你只提供了索引不提供內容,則會發生編譯時沒問題,鏈結時出問題。

extern 標頭檔案

很多情況下,程式設計師可能突然產生這樣的問題 為什麼非得在檔案的首位置 include h 我include原始檔行不行。single.cpp ifndef single cpp define single cpp include void fun endif single cpp main.cpp...

string標頭檔案與cstring標頭檔案

首先說string的標頭檔案 1 包裝了std的c 標頭檔案 2 舊的c標頭檔案 3 舊c標頭檔案的std版本,切記,這不是cstring的標頭檔案 詳見effective c 的第49條 再說cstring的標頭檔案 注 vc9編譯環境下 1 非mfc版本,控制台程式就用這個 2 mfc版本,需要...

C 標頭檔案與C標頭檔案

include 設定插入點 include 字元處理 include 定義錯誤碼 include 浮點數處理 include 檔案輸入 輸出 include 引數化輸入 輸出 include 資料流輸入 輸出 include 定義各種資料型別最值常量 include 定義本地化函式 include ...