我们在C代码中经常会使用#define去定义宏。在Linux内核代码或是一些比较大型的C项目中,我们经常会见到类似“do {...} while (0)”的宏。那么这样写到底是为什么呢?其实原因很简单——为了避免在不同场景中宏替换产生错误。我们都知道宏在代码预处理阶段会被展开,而这个展开就是简单的文本替换。这里是非常容易出错的。下面我们举一些例子来说明。

定义下面宏:

#define foo(x)	bar(x); baz(x)

某A处调用:

foo(wolf);

展开后使我们预期的:

bar(wolf); baz(wolf);

再看某B处调用:

if (!feral)
	foo(wolf);

展开后并不是我们预期的:

if (!feral)
	bar(wolf);
baz(wolf);

但是我们如果使用do{...}while(0)宏来写,就可以正常替换了:

#define foo(x)	do { bar(x); baz(x); } while(0)

这样刚才某B处的调用展开后为:

if (!feral)
	do { bar(wolf); baz(wolf); } while(0);

这显然就是对的了。看到这,也许有人会说,上面的语句不就等效于:

if (!feral)
	{ bar(wolf); baz(wolf); }

那我们将宏定义成如下样子不就可以了:

#define foo(x) { bar(x); baz(x); }

其实这样在某些场景下也是不行的:

if (!feral)
	foo(wolf);
else
	bin(wolf);

这样的语句展开后为:

if (!feral)
	{ bar(wolf); baz(wolf); };
else
	bin(wolf);

显然,是有语法错误的。所以使用do{…}while(0)的写法是非常具有通用性和安全的。

个人之见:现在C中已经有const和内联函数(inline)了,所以我们尽量就不要再使用#define宏去定义函数了,特别是一些复杂的函数,不仅不便于调试,而且也很容易出错。