// 危險定義
#define CAL(x, y) x + y
// 調用 CAL(2,4) * CAL(2,5) → 2 + 4 * 2 + 5 = 15 (預期 42)
// 安全定義:參數與整體雙重括號
#define CAL(x, y) ((x) + (y))
// 展開 ((2) + (4)) * ((2) + (5)) = 42 ✓
鐵律:宏參數在替換處必須加括號,宏整體定義建議加外層括號。
#define PI 3.14
#define PI 10.48 // 警告:"PI" redefined,最終值 10.48
避免重複定義;若需覆蓋,先 #undef PI 再定義。
#define int char // 災難:所有 int 變成 char
#define break continue // 循環邏輯全亂
嚴禁定義與關鍵字、標準庫函數同名的宏。
# 與 連接 ##| 操作符 | 作用 | 例子 | 展開結果 |
|---|---|---|---|
# | 參數 → 字符串字面量 | #define STR(x) #x | STR(abc) → "abc" |
## | 連接兩個標記為一個 | #define CAT(a,b) a##b | CAT(var, 1) → var1 |
#define A 100
#define STR(x) #x
#define STR_INDIRECT(x) STR(x) // 中間層強制展開參數
STR(A) → "A" (參數未展開直接字符串化)
STR_INDIRECT(A) → "100" (A 先展開為 100,再字符串化)
模式:#define PUBLIC_MACRO(x) INTERNAL_MACRO(x),內部宏執行 #/##,外部宏負責遞歸展開參數。
#define ADD(a,b) ((a)+(b)); // 尾部分號
x = ADD(1,2) + ADD(3,4); // 展開 x = ((1)+(2)); + ((3)+(4)); → 語法錯誤
宏體不含分號,調用處按語句規則加分號。
do { ... } while(0) 包裹
// 錯誤:if 後只跟第一句
#define DO(a,b) a+b; a++;
if (cond) DO(x,y); else ... // 語法錯
// 正確:do-while(0) 形成單一語句塊
#define DO(a,b) do { a+b; a++; } while(0)
if (cond) DO(x,y); else ... // 完美工作
__LINE__)
#define ANON_VAR(type) type _anon_##__LINE__
ANON_VAR(int); // → int _anon_42; (第42行)
#define ENTRY(name) {name, #name}
enum Cmd { OPEN, CLOSE };
struct Map { int id; const char *str; };
struct Map table[] = { ENTRY(OPEN), ENTRY(CLOSE) };
// → {{OPEN, "OPEN"}, {CLOSE, "CLOSE"}}
#define FILE_NAME_STR __FILE__
static const char *this_file = FILE_NAME_STR;
#define TYPE_BUF_SIZE(type) (sizeof(#type)) // 需兩層展開
char buf[TYPE_BUF_SIZE(INT_MAX)]; // → char buf[sizeof("2147483647")] → 11
__LINE__:當前行號 (int)__FILE__:當前文件名 (字符串)__DATE__:編譯日期 "Mmm dd yyyy"__TIME__:編譯時間 "hh:mm:ss"__STDC__:符合標準則為 1
#ifdef DEBUG
#define LOG(fmt, ...) printf("[%s:%d] " fmt "\n", __FILE__, __LINE__, ##__VA_ARGS__)
#else
#define LOG(fmt, ...)
#endif