do {} while(0)

来源:互联网 发布:知乎 a脑波音乐副作用 编辑:程序博客网 时间:2024/06/03 20:40

你也许看到了一个条件表达式是常量0的do循环.它创建了只会执行一次的的循环. 这种编程方法可以将多行的宏放在任何只能用一条语句的地方.

由于do循环的条件表达式是一个常量, 编译器不会生成额外的代码来处理循环, 所以当执行这段代码时, 这种方法不会增加多余的操作.

这种技术在宏定义中频繁使用, 尤其是宏的内容较复杂(如多条语句)时. 由于不知道宏被用在哪儿, 所以使宏满足被替换后所需要的语法很重要. 例如: 下面的宏用一条语句替换两条语句.

#define foo \        statement_1; \        statement_2

如果把此宏用到以下if语句中, 不会产生预期的效果:

if (condition)    foo;

替换宏后, if条件后面跟着两条语句, 这大概不是你想要的, 因为if语句只控制了第一条语句的执行.

if (condition)    statement_1;    statement_2;

接下来的例子使用do循环方法重写. 注意, 在定义中没有结尾的分号, 分号将会由被替换的语句提供.(译者注: 这样子使得调用宏的语句起来更像条合法的语句. 如果宏定义中有分号, 那么调用宏时不用加分号, 看起来会很唐突.)

#define foo \        do { \            statement_1; \            statement_2; \        } while (0)

现在, 使用该宏时, 正如你预期的那样, if条件后面跟的是一条语句.(译者注: 以下语句是展开后的结果)

if (condition)    do {        statement_1;        statement_2;    } while (0);

当宏中只包含if语句不包含else语句时, 也会发生类似的问题.

#define foo \        if (condition) \        statement

如果该宏用在有else语句的if语句中, 宏中的if将会偷走跟在它后面的else语句.

if (condition)    foo;else    bar;

虽然else语句的缩进表明它属于第一个if语句, 但是它与第二个else语句结合.

if (condition)    if (condition)        statement;else    bar;

如果在该例子中使用do循环方法, if语句将会在宏中结束, 所以不会偷走下面的else语句.

if (condition)    do {        if (condition)            statement;    } while (0);else    bar;

使用空的do-while(0)循环也是一种定义空语句宏的通用方法. 在旧编译器中, 防止编译器产生警告是有必要的, 例如:

#define do_nothing do {} while (0)

现在的gcc编译器提供了另一种可以替换do-while的方法, 例如:

#define foo ({ \        statement_1; \        statement_2; \})

翻译完毕.

译者注:以下是linux 2.4.31内核的max实现:

#define max(x,y) ({ \        const typeof(x) _x = (x);       \        const typeof(y) _y = (y);       \        (void) (&_x == &_y);            \        _x > _y ? _x : _y; })

为了避免重复计算max的参数, 宏定义中使用了typeof, typeof是GNU的扩展, 用来取表达式的类型, typeof貌似不会计算表达式的值. 我会另开帖子来介绍max函数.

原文链接:

Bruce Blinn: do {} while(0)

Reference:

Bit Twiddling Hacks

GCC doc: Referring to a Type with typeof