在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]
- Include防範 (wiki)
- pragma once (wiki)
- Redundant Include Guards