引入防護(include guard)
標頭檔常常引入其他標頭檔,所以同一個檔案很容易被貼進來兩次。想像你的兩個檔案都 #include 同一個 types.h;如果第三個檔案把兩者都拉進來,types.h 的內容就到達了兩次。對某些東西來說,例如定義一個 struct,得到兩次是徹底的錯誤:「redefinition(重複定義)」。引入防護是一個小小的標準技巧,讓標頭檔可以被引入任意多次而安全,只在第一次貼入它的本體,之後就跳過。
其機制是包住整個標頭檔的三行前置處理指令。在最上方你寫 #ifndef SOME_UNIQUE_NAME 接著 #define SOME_UNIQUE_NAME,在最底部寫 #endif。把它讀成一句話:「如果這個唯一名稱尚未被定義,就現在定義它並保留下面的一切;否則跳到底部。」標頭檔第一次被貼入時,名稱尚未定義,所以本體被保留、名稱被當作副作用定義了。之後每一次,名稱已被定義,所以整個本體被丟棄。名稱只需對該標頭檔唯一,慣例上類似 MYPROJECT_TYPES_H。
你日後打開的幾乎每個 C 與 C++ 標頭檔都以這個樣式開頭,而忘了它是編譯時令人費解的「redefinition」錯誤的常見原因。許多編譯器也接受在檔案最上方寫一行非標準的 #pragma once 來更簡短地達成同樣效果;它被廣泛支援,但不是官方語言的一部分,所以講求可攜性的程式碼常仍使用經典的三行防護。
#ifndef MYPROJECT_TYPES_H /* 若尚未定義…… */ #define MYPROJECT_TYPES_H /* ……就定義它並繼續 */ struct Point { int x, y; }; #endif /* MYPROJECT_TYPES_H */ /* 第二次引入會直接跳到這裡 */
無論標頭檔被拉進來幾次,本體都只被引入恰好一次。
防護名稱在整個專案中必須唯一;兩個標頭檔不小心共用同一個防護名稱,會悄悄地讓其中一個變空,因為第二個的本體總是被跳過。