[C/C++] Redundant Include Guards

在C/C++中重複的include往往會造成大災難

在C/C++中重複的include往往會造成大災難

如下面的例子所示:

header_1.h

struct node_t { int num; };

header_2.h

#include "header_1.h"

foo.c

#include "header_1.h"
#include "header_2.h"
由於在 foo.c 中 struct node_t 被間接地重複定義,導致程式經由compile後會造成compile error。

 

解決的方式是在程式中加入 #ifnde 與 #define,請參考下面的範例:

header_1.h

#ifndef HEADER_1_H_
#define HEADER_1_H_
struct node_t { int num; };
#endif

header_2.h

#ifndef HEADER_2_H_
#define HEADER_2_H_
#include "header_1.h"
#endif

foo.c

#include "header_1.h"
#include "header_2.h"

我們透過在header file中所加入的 #ifndef 與 #define 來讓 preprocessor 檢查,其中header_1.h第一次include會定義HEADER_1_H_。接著當header_2.h引入時,又再次引入header_1.h,preprocessor會發現#ifndef不成立並直接跳到#endif的部分,也避免了重複定義struct node_t。

不過利用這種方式必須要小心Macro Name的重複問題。

 

除此之外,還有另外一種no cross-platform的手段來解決Redundant Include,即是採用'#pragma once',參考下面範例:

header_1.h

#pragma once
struct node_t { int num; };

header_2.h

#include "header_1.h"

foo.c

#include "header_1.h"
#include "header_2.h"

利用#pragma once,compiler會自行比對檔名或inode而非去讀取Marco。透過這種高階的技巧,一來可以避免定義Macro有可能產生的名稱碰撞(name collisions),二來加快編譯速度。

然而,由於#pragma once並非是標準指令,所以compiler有可能不支援或是有不同的特性,這些都是要在使用前小心的部分。

 

[Reference]