在freeglut库中,我在文件中freeglut_callbacks.c
找到了以下代码:
/*
* All of the callbacks setting methods can be generalized to this:
*/
#define SET_CALLBACK(a) \
do \
{ \
if( fgStructure.CurrentWindow == NULL ) \
return; \
SET_WCB( ( *( fgStructure.CurrentWindow ) ), a, callback ); \
} while( 0 )
我不明白为什么这里需要一个循环,因为无论如何它只会执行一次,不是吗?
该构造广泛用于“功能性”宏,旨在将宏内的多个语句和声明组合成一个复合语句。这样的联合是必要的,这样宏就可以在条件运算符、循环等的分支中用作常规函数调用。无需不断地将调用包装在外部对中
{ ... }
乍一看,只需将宏定义本身
{ ... }
与配对即可实现相同的目的do { ... } while (0)
。但是,选项 c{ ... }
会有一个令人不快的缺点 - 需要不断记住他的身体是{ ... }
,然后{ ... }
在 C 中并不总是可以放;
.例如,如果 body
SET_CALLBACK
简单地包含在 中{ ... }
,那么这个看起来很自然的代码将不会编译,因为由于它后面的SET_CALLBACK(foo)
分号,分支else
被从它自己的分支中撕掉了if
这是它进入舞台的地方
do { ... } while (0)
。循环do/while
的独特之处在于它(几乎)是 C 语言中唯一形成块的语法结构,并且它总是无条件地以;
. 多亏了这个闭包;
,我们可以do { ... } while (0)
以更自然的方式使用复合宏,将它们组合成一条语句。在上面的示例中, cif
;
之后的 cSET_CALLBACK(foo)
将被构造“吸收”do { ... } while (0)
并且不会“破坏” 的完整性if
。这是为了能够通过我们调用普通函数的相同自然语法来“调用”函数宏,复合宏的主体不是包含在 中
{ ... }
,而是包含在do { ... } while (0)
PS为什么该语言的作者曾经决定坚持在
;
语法中包含一个[理论上完全没有必要的] 结束语,这do/while
一点并不完全清楚,但已经完成了。这是一个非常有用的结构。由于宏是“按原样”替换的,因此可能会发生各种奇怪的事情。假设我们有这样一个宏
(人为的例子)
如果现在这样打电话
然后它会展开成
当然,它根本不会像用户期望的那样工作——变量声明显然不存在并且没有足够的括号。
原则上,只用大括号就可以解决问题,但是带有的构造提供
do-while(0)
了一个重要的优势——使用 break 退出宏的能力,这使宏更接近于常规函数。