2017年5月7日 星期日

[C] 重複引用、重複定義問題

一般來說,為了方便在多個不同的.c檔(source file)共用相同的function, structure, ...
我們會將其宣告(declaration)在一個.h檔(header file)裡面,然後就可以在需要的.c檔引用該.h檔

這樣一來可以避免在每個.c檔都做一次宣告的麻煩
此外當需要對宣告做修改時也僅需要修改.h檔即可



例如我們希望宣告一個structure來表示一個pixel中的RGB三色,並讓此宣告可以橫跨多個.c檔使用
我們可以新增一個color.h檔,並在裡面加入此structure即可

color.h

#ifndef _COLOR_H_
#define _COLOR_H_


typedef struct ColorPixel
{
    int R;
    int G;
    int B;
} COLOR;

#endif // _COLOR_H_

為了避免重複引用(multiple inclusions)而出現redefinition的錯誤
通常我們會在.h檔加入macro (如上例color.h中藍色部分)
在程式編譯時,若發現_COLOR_H_已經被定義(define),則會忽略macro所包含的內容
如此就不會重複宣告COLOR這個structure了

而這個define的名稱習慣上會以檔案名稱前後加底線,並全部大寫來表示(dot也以底線取代)
例如color.h就會寫成_COLOR_H_
養成此習慣可以避免不同檔案不小心定義到相同的名稱,造成此機制失效

如此一來,我們就能在任意.c檔引入color.h來使用COLOR這個structure了


再來,使用.h檔的一個大原則就是只做宣告(declaration),不做定義(definition)
如果偷懶在.h檔做定義的話,當編譯時若產生多個.o檔(object file)的話
就會出現重複定義(multiple definition)的錯誤

color.h

#ifndef _COLOR_H_
#define _COLOR_H_

typedef struct ColorPixel
{
    int R;
    int G;
    int B;
} COLOR;

COLOR sFirstPixel = {255, 255, 255};

#endif // _COLOR_H_

如上例,我們在color.h裡面直接定義了一個變數sFirstPixel
此時又因為不同的.c檔同時引用color.h
造成每個.c檔編譯出來的.o檔都包含此變數,因此在linking階段就會出現重複定義(multiple definition)的錯誤

那如果想要讓sFirstPixel這個變數可以跨檔案使用怎麼辦?
在其中一個.c檔定義後,其他.c檔使用extern來存取
如下範例

A.c
#include "color.h"
COLOR sFirstPixel = {255, 255, 255};

B.c
#include "color.h"
extern COLOR sFirstPixel;
如此一來就還是可以跨檔案共用同一個變數囉~


最後再整理一下.h檔的使用好習慣
1. 每個.h檔加入各自的macro(#ifndef, #define, #endif)來防止重複引用,define命名時可使用檔名來避免取到相同名稱
2. 僅在.h檔做宣告(declaration)而不做定義(define)
3. 跨檔案使用的變數定義在.c檔,並搭配使用extern來共用

沒有留言:

張貼留言