C语言宏定义中为什么使用do while(0)

C语言的多行宏定义, 很多都使用 do {} while(0) 的形式. 比如 PHP 源码中就有很多这样的定义.
我们知道 do {} while 是循环结构, 在C语言中, 这种形式, 会先执行 do {} 中的语句, 再执行 while 中的判断条件. 也就是无论 while 中的条件是什么, do {} 中语句至少会执行一次. 而且 while(0), 所以 do {} 就只执行一次.

这是为什么呢?

我们来看一个例子:

1
#define TEST(a, b)  a++; b++;

这是个宏声明.

如果这个宏使用以下场景中

1
2
3
4
if (cond)
TEST(a, b);
else
...

这种情况下, 编译器会报错. 因为宏替换后, if () 后只包含 a++; 而 b_++; 会被编译器认为是 if 之外的语句.

这种情况也可以在 if 后问题带上 {} 来解决. 但是这不能从程序上完全保证.

那另外的解决方案是宏定义可以使用 {} . 但是这会引用另一个问题.
我们写程序, 不管是正常语句还是宏调用. 都习惯在最后写一个分号.
那如果宏后面有分号, 宏替换后, 会变成:

1
2
3
4
if (cond)
TEST(a, b);
else
...

宏替换后变成:
1
2
3
4
if (cond)
{ a++; b++; };
else
...

同样会编译不通过. 宏最后的分号会被认识是一个空语句, 而且是在 if () 包含之外.

如果换成 do {} while(0) 呢?
我们看一下

1
2
3
4
if (cond)
do { } while(0);
else
...

这样就没有问题, 编译会通完, 而且也没有歧义. 所以这是业界最佳实践.