在许多C/ c++宏中,我看到宏的代码被包装在似乎毫无意义的do while循环中。下面是一些例子。
#define FOO(X) do { f(X); g(X); } while (0)
#define FOO(X) if (1) { f(X); g(X); } else
我看不出他在做什么。为什么不干脆不写呢?
#define FOO(X) f(X); g(X)
在许多C/ c++宏中,我看到宏的代码被包装在似乎毫无意义的do while循环中。下面是一些例子。
#define FOO(X) do { f(X); g(X); } while (0)
#define FOO(X) if (1) { f(X); g(X); } else
我看不出他在做什么。为什么不干脆不写呢?
#define FOO(X) f(X); g(X)
当前回答
解释
执行{}while(0)和if (1) {} else确保宏被扩展到只有一条指令。否则:
if (something)
FOO(X);
将扩大为:
if (something)
f(X); g(X);
g(X)将在if控制语句之外执行。当使用do {} while(0)和if (1) {} else时,可以避免这种情况。
更好的选择
使用GNU语句表达式(不是标准C的一部分),你有一个比{}while(0)和if (1) {} else更好的方法来解决这个问题,只需使用({}):
#define FOO(X) ({f(X); g(X);})
并且此语法与返回值兼容(注意do {} while(0)不兼容),例如:
return FOO("X");
其他回答
解释
执行{}while(0)和if (1) {} else确保宏被扩展到只有一条指令。否则:
if (something)
FOO(X);
将扩大为:
if (something)
f(X); g(X);
g(X)将在if控制语句之外执行。当使用do {} while(0)和if (1) {} else时,可以避免这种情况。
更好的选择
使用GNU语句表达式(不是标准C的一部分),你有一个比{}while(0)和if (1) {} else更好的方法来解决这个问题,只需使用({}):
#define FOO(X) ({f(X); g(X);})
并且此语法与返回值兼容(注意do {} while(0)不兼容),例如:
return FOO("X");
虽然编译器应该优化掉do{…},(假);循环,还有另一个解决方案不需要这个结构。解决方案是使用逗号操作符:
#define FOO(X) (f(X),g(X))
或者更奇特:
#define FOO(X) g((f(X),(X)))
虽然这可以很好地用于单独的指令,但它不适用于构造变量并用作#define的一部分的情况:
#define FOO(X) (int s=5,f((X)+s),g((X)+s))
在这种情况下,将被迫使用do/while结构。
@jfm3 -你对这个问题的回答很好。你可能还想补充一点,宏习语还可以通过简单的'if'语句防止可能更危险的(因为没有错误)意外行为:
#define FOO(x) f(x); g(x)
if (test) FOO( baz);
扩展:
if (test) f(baz); g(baz);
这在语法上是正确的,因此没有编译器错误,但可能会产生意想不到的后果,即总是会调用g()。
Jens Gustedt的P99预处理器库(是的,这样一个东西的存在也让我大吃一惊!)改进了if(1){…} else构造了一个小而重要的方法,定义如下:
#define P99_NOP ((void)0)
#define P99_PREFER(...) if (1) { __VA_ARGS__ } else
#define P99_BLOCK(...) P99_PREFER(__VA_ARGS__) P99_NOP
这样做的基本原理是,与do{…} while(0) construct, break和continue仍然在给定的块内工作,但是((void)0)如果在宏调用后省略了分号,将会创建一个语法错误,否则将跳过下一个块。(这里实际上不存在“悬空else”问题,因为else绑定到最近的if,也就是宏中的if。)
如果您对C预处理器可以或多或少安全地完成的事情感兴趣,请查看该库。
我想没有提到过,所以考虑一下
while(i<100)
FOO(i++);
会被翻译成
while(i<100)
do { f(i++); g(i++); } while (0)
注意i++是如何被宏求值两次的。这可能会导致一些有趣的错误。