進階條件編譯技巧
1. #elif 鏈式判斷
#define LEVEL 2
#if LEVEL == 0
#define LOG_LEVEL "NONE"
#elif LEVEL == 1
#define LOG_LEVEL "ERROR"
#elif LEVEL == 2
#define LOG_LEVEL "WARN"
#elif LEVEL == 3
#define LOG_LEVEL "INFO"
#elif LEVEL == 4
#define LOG_LEVEL "DEBUG"
#else
#define LOG_LEVEL "UNKNOWN"
#endif
2. defined() 運算符組合條件
// 同時檢查多個宏是否定義
#if defined(DEBUG) && defined(VERBOSE)
#define TRACE(fmt, ...) fprintf(stderr, fmt, __VA_ARGS__)
#else
#define TRACE(fmt, ...)
#endif
// 檢查任一平台宏
#if defined(_WIN32) || defined(__linux__) || defined(__APPLE__)
// 支持的平台
#else
#error "Platform not supported"
#endif
3. #error / #warning 編譯期斷言
#if !defined(CONFIG_H)
#error "config.h must be included first"
#endif
#if VERSION < 2
#warning "Legacy version detected, consider upgrading"
#endif
#error:報錯並停止編譯。
#warning:輸出警告但繼續編譯(GCC/Clang/MSVC 支持)。
4. 預定義宏:編譯器提供的環境信息
| 宏 | 含義 | 示例 |
__FILE__ | 當前源文件名 | "main.c" |
__LINE__ | 當前行號 | 42 |
__DATE__ | 編譯日期 | "Jan 15 2025" |
__TIME__ | 編譯時間 | "14:30:00" |
__STDC__ | 是否符合 ANSI C | 1 |
__STDC_VERSION__ | C 標準版本號 | 201710L (C18) |
__func__ | 當前函數名 (C99) | "main" |
5. 變參宏 __VA_ARGS__ (C99)
#define LOG(fmt, ...) printf("[%s:%d] " fmt "\n", __FILE__, __LINE__, __VA_ARGS__)
LOG("Value = %d", x); // 單參
LOG("x=%d, y=%f", x, y); // 多參
// GNU 擴展:##__VA_ARGS__ 自動處理無參情況
#define LOG2(fmt, ...) printf(fmt, ##__VA_ARGS__)
LOG2("No args"); // 合法
本章綜合總結
- 預處理功能:編譯前文本處理,由預處理器自動完成,指令以
# 開頭。
- 宏定義:標識符映射替換文本。分無參/帶參。調用時文本替換(宏展開),非值傳遞。
- 宏定義避坑:宏名緊貼參數列表、參數必括、整體必括、多語句用
do{...}while(0)。
- 文件包含:
#include 插入文件內容。雙引號先找當前目錄,尖括號找系統目錄。允許嵌套,防重複包含用護衛宏或 #pragma once。
- 條件編譯:三形式
#ifdef/#ifndef/#if。按條件編譯部分代碼,減小目標體積、提升效率、實現跨平台/功能裁剪。
- 預處理運算符:
# 字符串化、## 連接、#@ 字符化、defined() 檢查宏定義。
- 工程價值:合理使用預處理,代碼更易修改、閱讀、移植、調試,支援模塊化設計。
現代 C 預處理最佳實踐
- 常量用
const/enum,函數用 static inline,盡量少寫宏。
- 條件編譯集中在
config.h,業務代碼無 #ifdef 雜質。
- 跨平台抽象層用宏隔離差異,上層調用統一 API。
- 調試宏
LOG 結合 __FILE__/__LINE__/__func__,發布版用空宏消除開銷。
- 啟用編譯器警告
-Wundef -Wendif-labels 捕獲宏濫用。