Not Only Algorithm,不仅仅是算法,关注数学、算法、数据结构、程序员笔试面试以及一切涉及计算机编程之美的内容 。。
你的位置:NoAlGo博客 » 程序设计 » 

do {…} while(0)

do {…} while(0)是很多项目,比如Linux内核源代码,中经常遇到的代码,表面上看毫无意义,但其中包含了一个巧妙的编程技巧。
本文将对这个问题做一个简单的分析。

简介

do {…} while(0)是一段让循环体内容只执行一次的代码,既然固定了只执行一次,那么为何还要加到循环里面呢?
其实这个技巧一般使用在宏定义之中,特别是当宏的内容包含了不止一句语句的时候。因为你不知道这个宏会被怎么使用,所以最好能够写出一个鲁棒性较好的实现,即其在大部分情况下都能被正常使用。

下面来看一个例子,我们使用宏来让foo(a,b)替代两句语句do_something1(a,b)和do_something2(a,b),其中do_something1和2是某些对a和b操作的代码。具体定义如下,其中末尾的反斜杠是续行用的,表示整体属于一个宏。

#define foo(a,b)        \
	do_something1(a,b); \
	do_something2(a,b)

那么,如果把该宏用于以下情景:

if (a == b)
	foo(a, b);

则实际上效果等价于:

if (a == b)
	do_something1(a,b);
	do_something2(a,b);

可以看到,程序的本意是想让a == b条件为真的时候才执行foo(a,b)表示的两句语句,为假的时候一句也不执行。但实际上do_something2(a,b)是永远都会执行的,因为do_something1(a,b)语句后面有一个分号,if语句的作用范围已经结束了。于是代码会产生难以发现的Bug。

再看一个例子,如果定义的宏的内容如下,注意这里没有加分号,因为默认的使用习惯会在语句末尾加上分号:

#define foo(a,b)         \
	if (a != b)          \
		do_something(a,b)

而使用场景为:

if (something)
	foo(a, b);
else
	do_otherthing();

那么实际上进行宏替换后的结果为:

if (something)
	if (a != b)
		do_something(a,b);
else
	do_otherthing();

可以看到,这里的do_othering本意是something不为真的时候执行,但实际上却是在something为真而a==b的时候执行了,因为else被匹配到了最接近的一个if语句即a!=b那里去了。

这里举的例子都是if语句的匹配问题,那么一个简单的解决方法就是给宏定义加上一个大括号。比如第一个例子中,把定义改为如下形式则可以正常工作。

#define foo(a,b)            \
	{                       \
		do_something1(a,b); \
		do_something2(a,b); \
	}

但是在第二个例子中,即使改成:

#define foo(a,b)               \
	{                          \
		if (a != b)            \
			do_something(a,b); \
	}	

也还是不能工作,因为按照一般习惯,我们使用foo(a,b)时要在后面加一个分号,那么宏展开时会在if和else中间多出一个分号,出现编译错误。

现在我们把宏定义改成:

#define foo(a,b)              \
	do {                      \
		if (a != b)           \
			do_something(a,b);\
	} while(0);

则不会出现以上的所有问题。

还有另外一个好处是可以定义空宏:

#define do_nothing  do {} while (0)

如果使用如下一般的方法定义则会在某些编译器上产生警告信息。

#define do_nothing 

目前GCC提供了另一种方法解决这个问题:

#define foo(a,b)            \
	({                      \
		do_something1(a,b); \
		do_something2(a,b); \
	})
上一篇: 下一篇:

我的博客

NoAlGo头像编程这件小事牵扯到太多的知识,很容易知其然而不知其所以然,但真正了不起的程序员对自己程序的每一个字节都了如指掌,要立足基础理论,努力提升自我的专业修养。

站内搜索

最新评论