巨集語句 do while 0

2021-06-19 01:12:13 字數 2088 閱讀 4747

有時程式中重複出現某幾條語句, 為了保證程式dry(don't repeat yourself), 需要將這幾條語句合併為一條語句.

在c++中可以選擇用 inline, c語言沒有這個特性, 但是可以用巨集實現.

正確答案是用dowhile(0)實現, 下面推理下為什麼這種方法最好吧.

思路一: 直接寫在一起

比如要將 i++; j++; 這兩條語句封裝在一起.

#define inc(i, j) i++; j++
這樣不滿足原子性, 在某些情況下兩條語句可能被拆開, 如:

if(1)

inc(i, j);

實際上被拆成了:

if(1)

i++;

j++;

思路二: 花括號

#define inc(i, j)
原子性滿足了, 但是仍然會在某些情況下出錯, 如:

if(bool)

inc(i, j);

else

/* code for else */;

此時相當於:

if(bool);

else

/* code for else */;

此時是會報錯的, else 被乙個空語句 ; 斷開了.

思路三: 逗號表示式

#define inc(i, j) i++, j++ // or: ( i++, j++ )
看上去還不錯, 程式會按照逗號表示式從左至右執行.

但是...如果想在巨集語句裡面定義區域性變數怎麼辦?

比方我想寫乙個 swap 函式交換兩個變數, 需要用乙個臨時變數.

#define swap(i, j) (int tmp, tmp = i, i = j, j = tmp)
這樣是無法編譯通過的...

而且, 逗號表示式中無法加入 if , break 等語句.

#define inc(i) if(1) i++ // ok

#define inc(i) (if(1) i++) // error

#define inc(i) (i++, if(1) i++) // error

#define inc(i) (i++, break, i++) // error

思路四: dowhile(0)

#define swap(i, j) dowhile(0)
這技巧其實很普遍, 人們說是linux核心中常用的技巧.

這麼寫至少有三條好處:

具有原子性, 語句不會被拆開;可以定義臨時變數;可以通過插入 break; 語句在該巨集語句的中間退出執行. 此技巧可用於異常控制, 類似 try...catch.

我第一次是在我編譯的 webkit 核心中見到的.

webkit 的詞法分析利用自動機的原理在狀態之間進行跳轉, 跳轉的過程中可能要頻繁進行一些操作, 如:

// htmltokenizer.cpp

#define html_reconsume_in(statename) reconsume_in(htmltokenizerstate, statename)

// markuptokenizerinlinemethods.h

#define reconsume_in(prefix, statename) \

do while (false)

這個**用來在狀態之間進行直接跳轉, 跳轉的過程中需要將當前狀態 m_state 設定為目標狀態 prefix::statename, 然後 goto 跳轉到該狀態.

巨集定義中的do while 0 語句

do while 0 在 linux 核心中,經常會看到 do while 0 這樣的語句,許多人開始都會疑惑,認為 do while 0 毫無意義,因為它只會執行一次,加不加 do while 0 效果是完全一 樣的,其實 do while 0 主要用於巨集定義中。這裡用乙個簡單點的巨集來演示 d...

巨集定義中的do while 0 語句

do while 0 在 linux 核心中,經常會看到 do while 0 這樣的語句,許多人開始都會疑惑,認為 do while 0 毫無意義,因為它只會執行一次,加不加 do while 0 效果是完全一 樣的,其實 do while 0 主要用於巨集定義中。這裡用乙個簡單點的巨集來演示 h...

巨集定義中的do while 0

如果你是c 程式設計師,我有理由相信你用過,或者接觸過,至少聽說過mfc,在mfc的afx.h檔案裡面,你會發現很多巨集定義都是用了do.while 0 或do.while false 比如說 define afxassume cond do while 0 粗看我們就會覺得很奇怪,既然迴圈裡面只執...