在宏定義中,# 置於參數前,會將該參數的原始語彙轉換為字串常值(雙引號包裹)。這在除錯追蹤、日誌輸出極為實用。
# 字串化(若參數本身是宏,會先展開)。#define TRACE(S) (printf("%s\n", #S), S) // 逗號表達式:先印字串,再返回 S 的值
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main() {
int a = 5;
int b = TRACE(a); // 輸出 "a",b = 5
const char *str = "hello";
char des[50];
strcpy(des, TRACE(str)); // 輸出 "str",des = "hello"
puts(des);
system("pause");
return 0;
}
a
str
hello
strcpy(des, TRACE(str)) 能複製字串內容?TRACE(str) 展開為 (printf("%s\n", "str"), str)。str(指標值)。strcpy 接收到的是 str 的指標,正常複製字串內容。注意區分:
int a = printf("%s\n", "S");中a是printf的返回值(印出字元數),而非字串 "S"。
雖未在原文提及,但常與 # 配合使用:## 可將兩個語彙連接成單一語彙(如生成變數名、函數名)。
#define CONCAT(a, b) a ## b
int CONCAT(var, 123) = 10; // 展開為 int var123 = 10;
// 字串化檔名、行號、運算式
#define ASSERT(expr) \
do { \
if (!(expr)) { \
fprintf(stderr, "Assertion failed: %s, file %s, line %d\n", #expr, __FILE__, __LINE__); \
abort(); \
} \
} while(0)
// 變數名與值同印
#define LOG_VAR(x) printf("%s = %d\n", #x, x)
int count = 42;
LOG_VAR(count); // 輸出: count = 42
| 誤區 | 正確理解 |
|---|---|
以為 #S 會展開 S 的值 | 只字串化「語彙本身」,不計算值。TRACE(a+b) 印出 "a+b" 而非 "15" |
在宏外使用 # | 編譯錯誤,# 只在宏替換列表中有效 |
| 忽略逗號表達式求值順序 | 左邊為副作用(如印字),右邊為整體表達式的值 |
善用 # 與逗號表達式,能寫出既高效又具自我描述能力的除錯宏,是 C 語言元程式設計的基礎利器。