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

C 語言 #define 宏定義完全指南:無參、帶參、運算符、嵌套、技巧與陷阱

     編輯:關於C語言

宏基礎:預處理階段的文本替換

#define 在**預處理期**做純文本替換,不進行語法檢查、類型檢查、作用域限制。替換後的代碼才進入編譯器。

無參數宏


#define PI 3.1415926
#define MAX_SIZE 1024
#define DEBUG_MSG "File: %s, Line: %d"
// 使用
float area = PI * r * r;
if (n > MAX_SIZE) ...
printf(DEBUG_MSG, __FILE__, __LINE__);
  • 行末**不加分號**——加了分號會連同分號一起替換,導致語法錯誤。
  • 2. 字符串可包含任何 token,預處理器原樣替換。

帶參數宏(函數式宏)


#define MAX(a,b) ((a) > (b) ? (a) : (b))
#define SQR(x)   ((x)*(x))
#define PRINT_INT(x) printf(#x " = %d\n", x)

關鍵規則

  1. 宏名與參數列表間不可有空格:#define MAX (a,b) ... 會被視為無參宏,宏體為 (a,b) ...。
  2. 參數全加括號:((a) > (b)) 防止運算符優先級錯亂。
  3. 整個宏體外層加括號:#define SQR(x) ((x)*(x)),否則 100/SQR(2) → 100/2*2 = 100 錯誤。
  4. 實參不求值直接替換:SQR(i++) → ((i++)*(i++)),i 增加兩次!函數調用只增一次。

宏運算符:#、##、#@

運算符作用範例結果
#參數字符串化(加雙引號)#define STR(x) #xSTR(abc) → "abc"
##Token 粘合#define CAT(a,b) a##bCAT(var,1) → var1
#@字符化(加單引號,GCC 擴展)#define CHAR(c) #@cCHAR(a) → 'a'

// 實用模式:自動生成變數名/函數名
#define DECLARE_VAR(type, name) type name##_var
#define LOG_VAR(name) printf(#name " = %d\n", name##_var)

DECLARE_VAR(int, foo);  // 展開: int foo_var;
LOG_VAR(foo);           // 展開: printf("foo" " = %d\n", foo_var);

宏的嵌套與遞歸


#define PI 3.14
#define AREA(r) (PI * (r) * (r))
// AREA(5) → (3.14 * (5) * (5))
  • 預處理器**不遞歸展開**同一宏(防無限遞歸),但會展開宏體內的**其他宏**。
  • #define FOO FOO 禁止自我遞歸,展開一次後停止。

條件編譯與調試技巧


#define DEBUG 1
#if DEBUG
  #define LOG(fmt, ...) printf("[DEBUG] " fmt "\n", ##__VA_ARGS__)
#else
  #define LOG(fmt, ...) ((void)0)
#endif

// 可變參數宏 (C99)
#define ERR_EXIT(msg) do { perror(msg); exit(EXIT_FAILURE); } while(0)
// do-while(0) 保證宏在 if-else 中語法安全

經典陷阱與避坑

  1. 副作用雙重求值:MAX(a++, b) → a 可能增兩次。對策:改用 inline 函數或 C++ 模板。
  2. 宏參數被字符串化時不再展開:#define STR(x) #x; STR(PI) → "PI" 而非 "3.14"。對策:二次宏 #define STR2(x) #x; #define STR(x) STR2(x)。
  3. ## 粘合後不再掃描:CAT(STD, IN) → STDIN,但 STDIN 若也是宏**不會**再展開。
  4. 宏無作用域:全局汙染,習慣用 PROJECT_MODULE_MACRO 前綴命名。

現代 C/C++ 替代方案

  • 常量 → constexpr / const 變數(有類型、有作用域、可調試)。
  • 函數式宏 → inline 函數 / 模板函數(類型安全、單次求值、可內聯)。
  • 編譯期計算 → constexpr 函數 / 模板元編程。
  • 條件編譯 → #if 仍不可替代,但盡量縮小範圍。
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved