在 C 語言開發中,調試輸出 (日誌、斷言、追蹤) 與發布版性能 存在天然矛盾。#ifdef DEBUG / #endif 配合宏,實現編譯期開關:
__VA_ARGS__)#ifdef DEBUG
#define LOG(fmt, ...) fprintf(stderr, "[DEBUG] %s:%d " fmt "\n", __FILE__, __LINE__, ##__VA_ARGS__)
#else
#define LOG(fmt, ...) ((void)0)
#endif
// 使用
LOG("Connection fd=%d, errno=%d", fd, errno); // Debug 時輸出,Release 時消失
#ifdef DEBUG
#define ASSERT(cond, fmt, ...) \
do { if (!(cond)) { \
fprintf(stderr, "[ASSERT] %s:%d: " #cond " failed: " fmt "\n", __FILE__, __LINE__, ##__VA_ARGS__); \
abort(); \
} } while(0)
#else
#define ASSERT(cond, fmt, ...) ((void)0)
#endif
// 使用
ASSERT(ptr != NULL, "ptr=%p, size=%zu", ptr, size);
#ifdef DEBUG #define CHECK_NULL(p) ASSERT((p) != NULL, "null pointer at %s:%d", __FILE__, __LINE__) #define CHECK_RANGE(idx, max) ASSERT((idx) >= 0 && (idx) < (max), "index %d out of range [0,%d)", idx, max) #else #define CHECK_NULL(p) ((void)0) #define CHECK_RANGE(idx, max) ((void)0) #endif
| 維度 | 宏 | 函數 |
|---|---|---|
| 編譯期消除 | ✅ 完全展開/刪除 | ❌ 函數調用開銷、代碼段佔用 |
| 文件/行號捕獲 | ✅ __FILE__ __LINE__ 自動展開 | ❌ 需手動傳遞或用 __builtin_return_address (非標準) |
| 表達式求值一次 | ⚠️ 需 do{...}while(0) 保護 | ✅ 參數預先求值 |
| 類型安全 | ❌ 無 | ✅ 有 |
| 調試器可見性 | ⚠️ 顯示展開後代碼 | ✅ 可斷點、單步 |
#define LOG_LEVEL_NONE 0
#define LOG_LEVEL_ERROR 1
#define LOG_LEVEL_WARN 2
#define LOG_LEVEL_INFO 3
#define LOG_LEVEL_DEBUG 4
#ifndef CONFIG_LOG_LEVEL
#define CONFIG_LOG_LEVEL LOG_LEVEL_INFO
#endif
#define LOG_IF(level, fmt, ...) \
do { if (CONFIG_LOG_LEVEL >= level) \
fprintf(stderr, "[%s] %s:%d " fmt "\n", #level, __FILE__, __LINE__, ##__VA_ARGS__); \
} while(0)
#define LOG_ERR(fmt, ...) LOG_IF(LOG_LEVEL_ERROR, fmt, ##__VA_ARGS__)
#define LOG_WARN(fmt, ...) LOG_IF(LOG_LEVEL_WARN, fmt, ##__VA_ARGS__)
#define LOG_INFO(fmt, ...) LOG_IF(LOG_LEVEL_INFO, fmt, ##__VA_ARGS__)
#define LOG_DBG(fmt, ...) LOG_IF(LOG_LEVEL_DEBUG, fmt, ##__VA_ARGS__)
工程落地建議
- 統一宏定義頭文件:
debug.h集中管理,避免分散定義不一致。- 構建系統集成:CMake
add_compile_definitions(DEBUG=1)或NDEBUG標準宏控制。- Release 版保留關鍵錯誤日誌:
LOG_ERR級別常駐,LOG_DBG剝離。- 嵌入式系統:輸出重定向至 UART/RTT/環形緩衝區,避免
fprintf阻塞。- 避免副作用表達式:
LOG("x=%d", x++)在 Release 下x++消失導致邏輯差異 —— 宏參數嚴禁副作用。總結
Debug 宏是 C 語言實現條件編譯、零開銷調試、上下文自動捕獲的核心手段。善用
do{...}while(0)、__VA_ARGS__、__FILE__/__LINE__、編譯器屬性,構建分級、可配置、生產級的診斷體系,是專業 C 開發者的基本功。