C語言條件編譯(#if,#ifdef,#ifndef,#endif,#else,#elif)

2020-07-16 10:04:27
條件編譯(conditional compiling)命令指定前處理器依據特定的條件來判斷保留或刪除某段原始碼。例如,可以使用條件編譯讓原始碼適用於不同的目標系統,而不需要管理該原始碼的各種不同版本。

條件編譯區域以 #if、#ifdef 或 #ifndef 等命令作為開頭,以 #endif 命令結尾。條件編譯區域可以有任意數量的 #elif 命令,但最多一個 #else 命令。以 #if 開頭的條件編譯區域具有下面的格式:
#if 表示式1
  [ 組1]
[#elif 表示式2
  [ 組2]]
...
[#elif 表示式n
  [ 組n ]]
[#else
  [ 組n+1 ]]
#endif

前處理器會依次計算條件表示式,直到發現結果非 0(也就是 true)的條件表示式。前處理器會保留對應組內的原始碼,以供後續處理。如果找不到值為 true 的表示式,並且該條件式編譯區域中包含 #else 命令,則保留 #else 命令組內的程式碼。

組 1、組 2 等程式碼段,可以包含任意 C 原始碼,也可以包含更多的命令,包括巢狀的條件式編譯命令。在預處理階段結束時,沒有被前處理器保留以用於後續處理的組會從程式中全部刪除。

#if 和 #elif 命令

作為 #if 或 #elif 命令條件的表示式,必須是整數常數前處理器表示式。這與普通的整數常數表示式不同,主要區別在於:

(1) 不能在 #if 或 #elif 表示式中使用型別轉換運算子。

(2) 可以使用預處理運算子 defined。

(3) 在前處理器展開所有宏,並且計算完所有 defined 表示式之後,會使用字元 o 替換掉表示式中所有其他識別符號或關鍵字。

(4) 表示式中所有帶符號值都具有 intmax_t 型別,並且所有無符號值都具有 uintmax_t 型別。字元常數也會受該規則的影響。intmax_t 和 uintmax_t 定義在標頭檔案 stdint.h 中。

(5) 前處理器會把字元常數和字串字面量中的字元與跳脫序列轉換成執行字元集中對應的字元。然而,字元常數在前處理器表示式和在後期編譯階段是否具有相同的值,取決於實現版本。

defined 運算子

一元運算子 defined 可以出現在 #if 或 #elif 命令的條件中。它的形式如下:
defined 識別符號
defined (識別符號)

如果指定的  identifier 是一個宏名稱(也就是說,它已被 #define 命令定義,並且未被 #undef 命令取消定義),則 defined 表示式會生成值 1。否則,defined 表示式會生成值 0。

defined 運算子相對於 #ifdef 和 #ifndef 命令的優點是:你可以在更大型的前處理器表示式中使用它的值。如下例所示:
#if defined( __unix__ ) && defined( __GNUC__ )
/* ... */
#endif

大多數編譯器會提供預定義宏,例如上例所使用的宏,它用來識別目標系統和編譯器。因此,在 Unix 系統中,通常預先定義好了宏 __unix__,而 GCC 編譯器則會預先定義好了宏 __GNUC__。類似地,微軟 Windows 平台上的 Visual C 編譯器會自動定義好宏 _WIN32 和宏 _MSC_VER。

#ifdef 和 #ifndef 命令

可以通過 #ifdef 和 #ifndef 命令測試某個宏是否已被定義。它們的語法是:
#ifdef 識別符號
#ifndef 識別符號

這等同於下面的 #if 命令:
#if defined 識別符號
#if !defined 識別符號

如果 identifier 不是宏名稱,則 #ifndef 識別符號後面的條件程式碼被保留。