#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__);
#define MAX(a,b) ((a) > (b) ? (a) : (b))
#define SQR(x) ((x)*(x))
#define PRINT_INT(x) printf(#x " = %d\n", x)
#define MAX (a,b) ... 會被視為無參宏,宏體為 (a,b) ...。((a) > (b)) 防止運算符優先級錯亂。#define SQR(x) ((x)*(x)),否則 100/SQR(2) → 100/2*2 = 100 錯誤。SQR(i++) → ((i++)*(i++)),i 增加兩次!函數調用只增一次。| 運算符 | 作用 | 範例 | 結果 |
|---|---|---|---|
# | 參數字符串化(加雙引號) | #define STR(x) #x | STR(abc) → "abc" |
## | Token 粘合 | #define CAT(a,b) a##b | CAT(var,1) → var1 |
#@ | 字符化(加單引號,GCC 擴展) | #define CHAR(c) #@c | CHAR(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 中語法安全
MAX(a++, b) → a 可能增兩次。對策:改用 inline 函數或 C++ 模板。#define STR(x) #x; STR(PI) → "PI" 而非 "3.14"。對策:二次宏 #define STR2(x) #x; #define STR(x) STR2(x)。CAT(STD, IN) → STDIN,但 STDIN 若也是宏**不會**再展開。PROJECT_MODULE_MACRO 前綴命名。constexpr / const 變數(有類型、有作用域、可調試)。inline 函數 / 模板函數(類型安全、單次求值、可內聯)。constexpr 函數 / 模板元編程。#if 仍不可替代,但盡量縮小範圍。