有時程式中重複出現某幾條語句, 為了保證程式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 粗看我們就會覺得很奇怪,既然迴圈裡面只執...