do {} while(0)

你也许看到了一个条件表达式是常量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

你可能感兴趣的:(do,while0)