C 語言的預處理器功能強大卻常被忽視。掌握 #error、字符串化運算符 #、連接運算符 ## 以及預定義宏 __FILE__、__LINE__,能讓你編寫出更健壯、可調試、可配置的代碼。本文通過實例逐一拆解這些高級特性。
當某些編譯條件不滿足要求時,#error 可強制終止編譯並輸出錯誤信息,防止錯誤配置流入後續流程。
// 要求 int 類型至少 4 字節(32 位系統通常滿足,16 位系統可能不滿足)
#if INT_MAX < 100000
#error "int 類型位寬不足,無法安全存儲業務 ID,請更換平台或使用 long"
#endif
應用場景:SDK 版本檢查、架構依賴驗證、禁止直接包含內部頭文件等。
# 將宏參數轉換為字符串字面量。結合 printf 可實現「變量名與值同步輸出」的調試宏,極大提升排查效率。
#define PRINT_INT(x) printf(#x " = %d\n", x)
int a = 10, b = 3;
PRINT_INT(a / b); // 展開為 printf("a/b" " = %d\n", a/b); 輸出:a/b = 3
進階技巧:結合 __FILE__、__LINE__ 可定義全功能調試宏:
#define DEBUG_LOG(fmt, ...) \
fprintf(stderr, "[DEBUG %s:%d] " fmt "\n", __FILE__, __LINE__, ##__VA_ARGS__)
## 可將兩個標記連接成一個新標記,廣泛用於生成重複性代碼模式,如聲明變量序列、構建類型相關函數名。
// 生成變量 i1, i2, i3
#define MK_ID(n) i##n
int MK_ID(1), MK_ID(2), MK_ID(3); // 展開為 int i1, i2, i3;
// 通用最大值宏:根據類型生成 type_max 函數
#define GENERIC_MAX(type) \
type type##_max(type x, type y) { return x > y ? x : y; }
GENERIC_MAX(int) // 生成 int int_max(int x, int y) { ... }
GENERIC_MAX(double) // 生成 double double_max(double x, double y) { ... }
這兩個預定義宏分別展開為當前源文件名(字符串)和當前行號(整數),是實現斷言、日誌、錯誤追蹤的基石。
void assert_failed(const char *expr, const char *file, int line) {
fprintf(stderr, "Assertion '%s' failed at %s:%d\n", expr, file, line);
abort();
}
#define ASSERT(expr) \
((expr) ? (void)0 : assert_failed(#expr, __FILE__, __LINE__))
還有 __DATE__(編譯日期)、__TIME__(編譯時間)、__STDC__(標準 C 合規性標誌)等輔助宏,構建版本信息較為便利。
#ifndef UTILS_DEBUG_H
#define UTILS_DEBUG_H
#include
#include
// 編譯期配置檢查
#if !defined(DEBUG) && !defined(NDEBUG)
#warning "未定義 DEBUG 或 NDEBUG,默認發布模式"
#endif
// 字符串化與連接演示
#define STR(x) #x
#define XSTR(x) STR(x)
#define TODO(msg) message(__FILE__ ":" XSTR(__LINE__) ": TODO: " msg)
// 斷言宏(發布版自動消失)
#ifdef NDEBUG
#define ASSERT(cond) ((void)0)
#else
#define ASSERT(cond) \
do { if (!(cond)) { \
fprintf(stderr, "ASSERT FAILED: %s (%s:%d)\n", #cond, __FILE__, __LINE__); \
abort(); \
} } while(0)
#endif
// 變量值打印
#define PRINT_VAL(x) printf("%s = %d\n", #x, x)
#endif // UTILS_DEBUG_H
預處理器不是簡單的文本替換工具,而是靜態元編程能力的體現。善用 #error 守門、# 反射變量名、## 消除樣板代碼、__FILE__/__LINE__ 定位問題,能讓 C 代碼在安全性、可維護性、開發效率上躍升一個台階。建議收藏常用宏模式,構建個人/團隊的「預處理器工具箱」。