close
C/C++ 的 Header file 不適合在裡面宣告實體物件的。因為做為一個 header file 可能會有很多程式檔含入(include)該Header file。如果你宣告了一個物件在裡面,編譯器會給你一個難堪的"重覆定義"的錯誤訊息。

通常的做法是,你應宣告全域物件在各自所屬的程式檔中,而其它要用到該物件的程式則宣告該全域物件為 external 即可。

現在的問題是,若有一個全域物件有許多的程式檔要用到,例如二十個檔案要使用,那麼要二十個檔案都要寫 external 宣告是非常麻煩的事!如果編譯器能聰明一點,在Header file宣告了全域變數,它
實 際只產一個物件,而其它所有含入這 Header file 的程式就全部以external 在參考那唯一產生的物件,豈不佳妙!?其實也真的有這樣的編譯器來幫忙"懶惰"的程式師(如 MS C 5.x 以前的版本)。但這是不正規的編譯器,也會延生其它問題(例如程員把原本應為兩個不同的物件卻寫成了同名)。所以絕大部份的編譯器是不允許你宣告物件在 Header file 給兩個以上的程式檔含入的。

//程式檔 MyLib.C

code:
#define MyLib_C
#include ".........."
#include "MyLib.H"
#undef MyLib_C
::::::::::::::::::::::::::::::::::::::::::
int function1(...)
{
:::::::::::::::::
}
int function2(...)
{
:::::::::::::::::
}
::::::::::::::::::::::::::::::::::::::::::


//其它含入 MyLib.H 的程式檔

code:
#include ".........."
#include "MyLib.H"
#include ".........."
::::::::::::::::::::::::::::::::::::::::::


//Header file MyLib.H

code:
::::::::::::::::::::::::::::::::::::::::::
int function1(...);
int function2(...);
#ifdef MyLib_C
#define AUTOEXT
#else //MyLib_C
#define AUTOEXT extern
#endif //MyLib_C
AUTOEXT int LastErrorNo;
::::::::::::::::::::::::::::::::::::::::::
#undef AUTOEXT



這樣子MyLib.C本身和其它用到MyLib.C全域物件的程式檔都可以放心
的含入 MyLib.H 了!

____________________________________________________________________________________
這 種方式是所謂的 Header Shield, 一般常見不是這樣用的(基本上, 這個例子是畫蛇添足 ), 主要常用在避免 header 循環 include 的問題, 比如 A.h include B.h, B.h include C.h, C.h include A.h, 可能不知道他們比次的關係造成一個 loop 了, 於是你在某個本文檔 #include &quto;A.h&quto; 便會出現問題, preprocessor 在處理 #include directive 時會有陷入循環的情形.

好的習慣是在寫每一個 header 都為其準備一個 shield:

code:
/* A.h */
#ifndef __A_H__
#define __A_H__

#include
#include
/* var/function declaration

#endif


B.h/C.h 也都是仿照這樣的方式, 照樣就可以避免循環引入 header 的問題.

另 外這種 header shield 也有用來作條件式編譯, 或是作 cross compile 的工作, 你在 A 平台用某工具(SDK) make 出 A 平台的可執行檔作測試, 要 release 時得 cross compile 成 target 平台的可執行檔, 但因平台差異可能需要對 source code 作些調整, 當然不是每次都去動 source 囉, 就可以利用這些技巧.

____________________________________________________________________________________
ANSI C 本來就規定 extern int LastErrorNo; 宣告之後可以出現 lastErrorNo 的定義, 所以你可以把

code:
extern int LastErrorNo;


寫 在 MyLib.h, 在任何需要 MyLib 的本文檔 include MyLib.h, 把 LastErrorNo 的定義寫在 MyLib.c 檔即可, 當然 MyLib.c 是一定會 include MyLib.h 的, 於是 LastErrorNo 的宣告和定義會出現在同一個本文檔中, 但這是正規的 ANSI C 寫法, 這樣本來就是符合惟一定義的規則.
把這一行:

code:
int LastErrorNo;


寫在 MyLib.c 檔中.

你想想看為什麼可以在 .c 檔案前頭先宣告:

code:
int add(int, int);



然後在之後才定義 add function 呢?

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



變 數和函式都是一樣的, 如果你想避免你的函式及變數不必程式其他部分使用, 有點像 OOP 的隱藏作用, 你就在變數及函式定義時加上 static, 當然 header 就不需要提供這些不想被連結的變數或函式的宣告; 以 static 來指定內部連結性之後, 即使程式他處有出現同名變數或函式的宣告, 如果 linker 在所有的本文檔都找不到其定義, 就會有 linker error, 絕不會連結到你不想被連結的部分(static 修飾過的).


arrow
arrow
    全站熱搜
    創作者介紹
    創作者 lver76 的頭像
    lver76

    Iver's Blog

    lver76 發表在 痞客邦 留言(0) 人氣()