首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> C語言 >> C >> 關於C

C語言預處理器完全指南:宏定義、檔案包含、條件編譯與進階技巧

     編輯:關於C

前言:預處理階段做了什麼?

預處理(Preprocessing)發生在編譯器詞法掃描與語法分析之前,由預處理器自動處理所有以 # 開頭的指令。主要功能包括:

  • 宏定義(#define)
  • 檔案包含(#include)
  • 條件編譯(#ifdef/#if/#ifndef)
  • 其他:#undef、#error、#pragma、#line 等

善用預處理可提升程式碼可讀性、可移植性、模組化程度與除錯效率。

宏定義深度解析

無參宏(物件樣宏)

#define IDENTIFIER token-sequence
  • 純文字替換,預處理器不做語法檢查。
  • 慣例宏名全大寫,行尾不加分號(否則分號也會被替換)。
  • 可嵌套,可用 #undef 終止作用域。
  • 陷阱:#define pint (int*) 導致 pint a, b; 展開為 int* a, b;(b 非指標)。應改用 typedef int* pint;。
  • 優勢:無型別限制,可用於任何型別;劣勢:無型別安全,副作用難控(如 MAX(a++, b) 導致 a++ 執行多次)。

帶參宏(函數樣宏)

#define MACRO(params) token-sequence-with-params
  • 宏名與左括號間不可有空格,否則視為無參宏。
  • 形參不佔記憶體、無型別,實參直接文字替換(無值傳遞語意)。
  • 括號防禦原則:形參兩側加括號、整個展開式外層加括號。
    正確範例:#define SQ(x) ((x)*(x))
  • 副作用陷阱:SQ(i++) 展開為 ((i++)*(i++)),i 遞增兩次;函數呼叫 Square(i++) 則只遞增一次。
  • 遞迴展開保護:宏調用自身時停止展開(#define TEST(x) (x + TEST(x)) → 1 + TEST(1))。

進階宏技巧

字串化運算子 #

將實參轉為字串常值:#define STR(x) #x → STR(abc) 展開為 "abc"。忽略前後空白,內部多空白壓縮為單空白。

符號連接運算子 ##

連接兩個 Token:#define PASTE(a,b) a##b → PASTE(var, 1) → var1。可連接多個 Token(a##_##b##_##c)。

多層展開技巧

若需先展開實參再套用 #/##,需引入中介宏:

#define _STR(s) #s
#define STR(s) _STR(s)   // 先展開 s,再字串化
STR(INT_MAX) → "2147483647"

可變參數宏 (C99)

#define LOG(fmt, ...) fprintf(stderr, fmt, __VA_ARGS__)

GCC 擴展支援 ##__VA_ARGS__ 自動移除前置逗號;亦可顯式命名 args...。

實用宏範例庫

用途宏定義
常數#define PI 3.1415926
最大/最小值#define MAX(a,b) (((a)>(b))?(a):(b))
結構體成員偏移#define OFFSET(type, member) ((int)&(((type*)0)->member))
陣列元素數#define ARR_SIZE(arr) (sizeof(arr)/sizeof((arr)[0]))
字元轉大寫#define TOUPPER(c) ((c)>='a'&&(c)<='z'?(c)-0x20:(c))
除錯追蹤#define TRACE(fmt, ...) printf("[%s(%d)<%s>] " fmt, __FILE__, __LINE__, __func__, ##__VA_ARGS__)
多語句安全包裹#define DO(...) do{__VA_ARGS__}while(0)
泛型交換#define SWAP(t, x, y) do{ t tmp=*(y); *(y)=*(x); *(x)=tmp; }while(0)

檔案包含:#include

  • #include <header.h>:搜尋系統標準包含目錄。
  • #include "header.h":先搜尋當前源檔目錄,再搜尋系統目錄。
  • 允許嵌套包含;防止重複包含請見條件編譯「防護宏」模式。

條件編譯:三大形式

1. #ifdef / #ifndef

#ifdef MACRO
  // MACRO 已定義時編譯
#else
  // 未定義時編譯
#endif

2. #if / #elif / #else

#if EXPRESSION != 0
  // 真
#elif OTHER_EXPR != 0
  // 另一條件真
#else
  // 假
#endif

EXPRESSION 必須為整數常數運算式(可含 defined(MACRO))。

實戰應用場景

  1. 跨平台型別抽象:#ifdef _WIN32 ... #else ... #endif
  2. 功能模組裁剪:#ifdef FEATURE_FLV ... #endif
  3. 除錯開關:#ifdef DEBUG ... #endif,發布版移除 #define DEBUG 即可。
  4. 硬體模擬:#ifndef TEST ... #else i=0; #endif
  5. 頭檔防護宏(標準模式):
    #ifndef __HEADER_H__
    #define __HEADER_H__
    // 宣告、定義
    #endif
  6. 避免頭檔定義變數:頭檔僅 extern int var;,單一 .c 檔 int var = 0; 定義,否則 C++ 連結器報重複定義錯誤。

ANSI 預定義宏

  • __LINE__:當前行號(整數)
  • __FILE__:當前源檔名(字串)
  • __DATE__:編譯日期 "Mmm dd yyyy"
  • __TIME__:編譯時間 "hh:mm:ss"
  • __STDC__:標準 C 合規性標誌(1 表合規)
  • 常見擴展:__func__(C99)、__FUNCTION__(GCC/MSVC)

總結

預處理器是 C 語言獨特的元編程層,掌握宏定義的替換語意、條件編譯的編譯期決策、檔案包含的依賴管理,能寫出更彈性、可移植、易維護的程式碼。切記:宏非函數,無型別、無作用域、無副作用控制,濫用易生難測 Bug,關鍵路徑優先考慮 inline 函數或 constexpr (C++)。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved