【转载请注明出处: http://blog.csdn.net/lzl124631x/article/details/14222113】
有时程序中重复出现某几条语句, 为了保证程序DRY(Don't Repeat Yourself), 需要将这几条语句合并为一条语句.
在C++中可以选择用 inline, C语言没有这个特性, 但是可以用宏实现.
正确答案是用 do{ /* Your Code Here. */ }while(0) 实现, 下面推理下为什么这种方法最好吧.
思路一: 直接写在一起
比如要将 i++; j++; 这两条语句封装在一起.
#define INC(i, j) i++; j++
这样不满足原子性, 在某些情况下两条语句可能被拆开, 如:
if(1)
INC(i, j);
实际上被拆成了:
if(1)
i++;
j++;
思路二: 花括号
#define INC(i, j) { i++; j++; }
原子性满足了, 但是仍然会在某些情况下出错, 如:
if(bool)
INC(i, j);
else
/* Code For Else */;
此时相当于:
if(bool){
i++; j++;
};
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
思路四: do{/*...*/}while(0)
#define SWAP(i, j) do{int tmp = i; i = j; j = tmp; }while(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 { \
m_state = prefix::stateName; \
goto stateName; \
} while (false)
这个代码用来在状态之间进行直接跳转, 跳转的过程中需要将当前状态 m_state 设置为目标状态 prefix::stateName, 然后 goto 跳转到该状态.