Linux 內核大量使用 do { ... } while(0) 包裹多語句宏,確保宏在 if/else、迴圈、函數等任何控制結構中都能像單一語句一樣工作,避免「悬空 else」或多語句僅執行首行的經典陷阱。
{ ... }?#define DBG(fmt, ...) { fprintf(stderr, fmt, ##__VA_ARGS__); }
若寫 if (cond) DBG("err"); else ...,展開後變為 if (cond) { ... }; else ... —— 分號導致 else 無法匹配,編譯報錯。
#define DBG(fmt, ...) do { fprintf(stderr, fmt, ##__VA_ARGS__); } while(0)
展開後:if (cond) do { ... } while(0); else ... —— do-while 是單一語句,後接分號合法,else 正常匹配。且 while(0) 保證循環體**恰好執行一次**(do 先執行再判斷)。
int main() {
while(0) { printf("hello world\n"); } // 不執行
do { printf("hello world1\n"); } while(0); // 執行一次
return 0;
}
#include
#include
#define DB_ERR(fmt, ...) do { fprintf(stderr, "(ERR): "); fprintf(stderr, fmt, ##__VA_ARGS__); } while(0)
#define DB_MSG(fmt, ...) do { fprintf(stdout, "(MSG): "); fprintf(stdout, fmt, ##__VA_ARGS__); } while(0)
#define DB_WARN(fmt, ...) do { fprintf(stdout, "(WARN): "); fprintf(stdout, fmt, ##__VA_ARGS__); } while(0)
#define DB_DBG(fmt, ...) do { fprintf(stdout, "(DBG): "); fprintf(stdout, fmt, ##__VA_ARGS__); } while(0)
int main() {
DB_ERR("Failed to open %s\n", "file.txt");
DB_WARN("Retry count %d\n", 3);
DB_DBG("Enter func()\n");
return 0;
}
(ERR): Failed to open file.txt (WARN): Retry count 3 (DBG): Enter func()
if/else/for/while 任意控制流,無需大括號包裹。int tmp;)不會汙染外部作用域。while(0) 為常數條件,編譯器會完全消除循環開銷,生成代碼等同於直接展開語句塊。

return/break/continue/goto 跳出 do-while,會破壞外層流程。do { ... } while(0),單語句宏可直接寫或用 (void)0。##__VA_ARGS__ 與 __VA_ARGS__ 差異(GNU 延伸)。