在 Linux 內核、驅動開發、嵌入式 C 項目中,do { ... } while (0) 是宏定義的標準守護模式。本文深度解析其解決的三大核心問題:語句級語法安全、變量作用域隔離、避免 if-else 懸空,並提供工程級調試宏模板。
#define DBG_LOG(fmt) printf(fmt); printf("\n")
// 調用時:
if (cond)
DBG_LOG("hello"); // 展開為:
// if (cond) printf("hello"); printf("\n"); // 只有第一句受 if 控制!
#define SWAP(a,b) { int t=a; a=b; b=t; } // 單獨大括號
// SWAP(x,y); // 語法錯誤:{} 不是表達式,不能後跟分號作為語句
#define DBG_LOG(fmt) \
do { \
printf(fmt); \
printf("\n"); \
} while (0)
// 展開後:
if (cond)
do { printf("hello"); printf("\n"); } while (0);
// 整個 do-while 是一條語句,受 if 完整控制,語法合法
// 內核標準調試宏(簡化版)
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#define pr_err(fmt, ...) \
do { \
printk(KERN_ERR pr_fmt(fmt), ##__VA_ARGS__); \
} while (0)
#define pr_warn(fmt, ...) \
do { \
printk(KERN_WARNING pr_fmt(fmt), ##__VA_ARGS__); \
} while (0)
#define pr_info(fmt, ...) \
do { \
printk(KERN_INFO pr_fmt(fmt), ##__VA_ARGS__); \
} while (0)
#define pr_debug(fmt, ...) \
do { \
if (DEBUG) printk(KERN_DEBUG pr_fmt(fmt), ##__VA_ARGS__); \
} while (0)
#define LOG(fmt, ...) printf(fmt, ##__VA_ARGS__)
// LOG("hello") -> printf("hello") ✓ 無多餘逗號
// LOG("val=%d", x) -> printf("val=%d", x) ✓ 正常
// 不加 ## 時:LOG("hello") -> printf("hello",) ✗ 語法錯誤
#include#include // 顏色碼(終端支持) #define RED "\033[31m" #define GREEN "\033[32m" #define YELLOW "\033[33m" #define BLUE "\033[34m" #define RESET "\033[0m" #define DBG_ERR(fmt, ...) \ do { \ fprintf(stderr, RED "[ERR][%s:%d] " fmt RESET "\n", __FILE__, __LINE__, ##__VA_ARGS__); \ } while (0) #define DBG_WARN(fmt, ...) \ do { \ fprintf(stdout, YELLOW "[WARN][%s:%d] " fmt RESET "\n", __FILE__, __LINE__, ##__VA_ARGS__); \ } while (0) #define DBG_INFO(fmt, ...) \ do { \ fprintf(stdout, GREEN "[INFO][%s:%d] " fmt RESET "\n", __FILE__, __LINE__, ##__VA_ARGS__); \ } while (0) #define DBG_DEBUG(fmt, ...) \ do { \ fprintf(stdout, BLUE "[DBG][%s:%d] " fmt RESET "\n", __FILE__, __LINE__, ##__VA_ARGS__); \ } while (0) // 測試 int main() { DBG_ERR("磁盤寫入失敗: %s", "I/O error"); DBG_WARN("內存使用率超過 80%%"); DBG_INFO("服務啟動完成, PID=%d", getpid()); DBG_DEBUG("進入處理循環, count=%d", 42); return 0; }
[ERR][main.c:45] 磁盤寫入失敗: I/O error [WARN][main.c:46] 內存使用率超過 80% [INFO][main.c:47] 服務啟動完成, PID=12345 [DBG][main.c:48] 進入處理循環, count=42
| 方案 | 語法安全 | 作用域隔離 | 表達式上下文 | 推薦度 |
|---|---|---|---|---|
do{}while(0) | ✓ | ✓ (內部可定義變量) | ✗ (是語句非表達式) | ★★★★★ 標準 |
{} 單獨大括號 | ✗ (分號問題) | ✓ | ✗ | ★☆☆☆☆ 不推薦 |
if(1){}else | ✓ | ✓ | ✗ | ★★★☆☆ 兼容舊代碼 |
({}) GCC 語句表達式 | ✓ | ✓ | ✓ (可作為表達式) | ★★★★☆ 非標準,僅 GCC/Clang |
do { ... } while (0) 在語法上是單條語句,可安全用於 if/else/for/while 受控塊。##__VA_ARGS__ 解決零參數時逗號殘留,C99/GNU 擴展標準。掌握 do-while(0) 宏守護模式,是編寫專業級 C 宏庫、內核模塊、嵌入式 SDK 的必備技能。它將「預處理器文本替換」的脆弱性,轉化為「語法級安全」的工程保障。