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

C語言預處理器詳解(二):帶參宏、多語句宏與宏函數對比

     編輯:C 語言基礎

帶參宏定義

#define MACRO_NAME(param-list) replacement-text
  • 宏名與參數列表間不能有空格:#define MAX(a,b) ✓;#define MAX (a,b) ✗(視為無參宏,替換文本為 (a,b))。
  • 形式參數不分配內存、無類型,只是替換標記。
  • 實際參數可以是任意表達式,原樣替換形參位置。

基本示例

#define MAX(a,b) ((a) > (b) ? (a) : (b))
#define SQUARE(x) ((x)*(x))

int m = MAX(3+2, 10);   // 展開:((3+2) > (10) ? (3+2) : (10)) -> 10
int s = SQUARE(a+1);    // 展開:((a+1)*(a+1))

帶參宏的關鍵括號規則(升級版)

  1. 形參兩側加括號:(a) 防止 SQUARE(a+1) 展開為 a+1*a+1。
  2. 整個替換文本外層加括號:((a)>(b)?(a):(b)) 防止 160/SQUARE(a+1) 優先級錯誤。
  3. 多語句宏用 do { ... } while(0) 包裹(見下)。

經典錯誤演進

// 1. 無括號
#define SQ(y) y*y
// 2. 參數括
#define SQ(y) (y)*(y)
// 3. 整體括
#define SQ(y) ((y)*(y))
// 4. 終極安全(參數括+整體括)
#define SQ(y) (((y)*(y)))

多語句宏:do-while(0) 慣用法

問題:多語句宏若不加大括號,用在 if 後會出錯。

// 錯誤寫法
#define SAFE_FREE(p) free(p); p = NULL;
if (ptr) SAFE_FREE(ptr);  // 僅 free(ptr) 受 if 控制,p=NULL 無條件執行

// 正確寫法
#define SAFE_FREE(p) do { free(p); p = NULL; } while(0)
if (ptr) SAFE_FREE(ptr);  // 整個 do-while 視為單一語句

原理:do { ... } while(0) 執行一次,無循環語義,語法上是單一語句,末尾需加分號,完美融入控制流。

帶參宏 vs 函數:深度對比

參數求值差異

#define SQ_MACRO(y) ((y)*(y))
int sq_func(int y) { return y*y; }

int i = 1;
printf("%d ", SQ_MACRO(i++));  // (1)*(2) = 2, i 變 3
printf("%d ", sq_func(i++));   // 3*3 = 9, i 變 4
  • 宏:參數不求值,直接文本替換,副作用多次發生。
  • 函數:參數求值一次,值傳遞,副作用可控。

類型安全


#define MAX_MACRO(a,b) ((a)>(b)?(a):(b))
int max_func(int a, int b) { return a>b?a:b; }

double x = 3.14, y = 2.71;
MAX_MACRO(x, y);   // 返回 double,但比較邏輯可能不精確
max_func(x, y);    // 隱式轉 int,丟失精度 -> 編譯器警告

宏「類型無關」看似靈活,實則隱患;函數強制類型檢查更安全。

調試與遞歸

  • 宏展開後行號混亂,斷點難設置,變量名消失。
  • 宏不支持遞歸(展開無限);函數天然支持。

何時使用宏

  1. 編譯期常量:#define VERSION "1.2.3"。
  2. 編譯期計算:#define ARRAY_SIZE(arr) (sizeof(arr)/sizeof((arr)[0]))。
  3. 類型無關的微小操作:#define SWAP(a,b) do{typeof(a) t=a; a=b; b=t;}while(0)(GNU 擴展)。
  4. 條件編譯配合:#ifdef DEBUG #define LOG(...) printf(__VA_ARGS__) #else #define LOG(...) #endif。

總結

帶參宏是帶參數的文本模板。牢記「宏名緊貼括號、參數必括、整體必括、多句用 do-while(0)」,並時刻警惕副作用多次求值與無類型檢查。現代 C(C99/C11)推薦:static inline 函數替代宏,const/enum 替代常量宏,_Generic 實現類型泛型——唯有條件編譯仍屬宏專屬領域。

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