`#define` 在**預處理階段**進行**標記級**替換,非文本簡潔替換。理解此點是避開所有陷阱的鑰匙。
#define N 5
int arr[N]; // → int arr[5];
#define N = 5
int arr[N]; // → int arr[= 5]; // 語法錯誤!
#define N 5;
int arr[N]; // → int arr[5;]; // 語法錯誤!
鐵律:替換內容**原樣放入**,不要加分號、等號、多餘符號。
// 面積計算
#define AREA(x) ((x)*(x)) // 正確
// 錯誤示範
#define AREA(x) x*x
int s = AREA(3+2); // → 3+2*3+2 = 15 (非 25)
// 封裝現有函數
int mult(int x, int y) { return x*y; }
#define SQUARE_AREA(x) mult(x, x)
#define RECT_AREA(x, y) mult(x, y)
防禦性寫法:每個參數加括號、整體表達式再加括號。
| 操作符 | 名稱 | 功能 | 範例 |
|---|---|---|---|
# | 字符串化 | 參數變為字符串字面量 | `#define STR(x) #x` → `STR(hello)` → `"hello"` |
## | 標記粘合 | 合併兩個標記為一個 | `#define CAT(a,b) a##b` → `CAT(var,1)` → `var1` |
__VA_ARGS__ | 可變參數 | 配合 `...` 實現可變參宏 | `#define LOG(...) printf(__VA_ARGS__)` |
#define PRINT_EXPR(expr) printf(#expr " = %d\n", expr)
PRINT_EXPR(3*4+5); // → printf("3*4+5" " = %d\n", 3*4+5); → 輸出:3*4+5 = 17
#define DECLARE_VAR(num) int var##num = num
DECLARE_VAR(1); // → int var1 = 1;
DECLARE_VAR(2); // → int var2 = 2;
#define LOG(level, fmt, ...) \
fprintf(stderr, "[%s] " fmt "\n", level, ##__VA_ARGS__)
LOG("ERROR", "File %s line %d: %s", __FILE__, __LINE__, "open failed");
// GCC 支持 `##__VA_ARGS__` 自動處理前導逗號(空參數時不留逗號)
/* 數組長度(僅適用於堆上數組/字面量,不適用指針) */
#define ARRAY_LEN(arr) (sizeof(arr)/sizeof((arr)[0]))
/* 結構體成員偏移 */
#define OFFSET_OF(type, member) ((size_t)&((type*)0)->member)
/* 容器指針從成員指針反推 */
#define CONTAINER_OF(ptr, type, member) \
((type*)((char*)(ptr) - OFFSET_OF(type, member)))
/* 最大/最小值(GNU 擴展 typeof 避免副作用) */
#define MAX(a,b) ({ typeof(a) _a=(a), _b=(b); _a>_b?_a:_b; })
掌握宏的本質與邊界,善用預處理器能力,克制濫用,方能寫出健壯、可維護的 C 代碼。