Significance and use of do{...}while(0)

英文原文:http://www.pixelstech.net/article/1390482950-do-%7B-%7D-while-%280%29-in-macros

对于一个C programmer,宏命令再熟悉不过了,宏的功能非常强大,只要使用正确它会极大地减轻你的工作量。

可是,只要一不小心,宏也是非常危险的,它会让你陷入彻底的纠结和疯狂。在很多C程序中,你会见到很多看起来不是很直接的特殊的宏定义,如:

#define __set_task_state(tsk, state_value)      \
    do { (tsk)->state = (state_value); } while (0)
这种写法在Linux内核和C库源码中非常常见,那么这种宏定义的用意究竟何在呢?目前就职于Google linux内核开发部的 Robert Love 给出了答案:

do {...} while (0)是C语言中唯一的一种构造(construct)来让宏命令始终如一的以一种正确的方式工作,即无论如何使用宏命令(特别强调的一种情况是用在没有花括号的if语句后面),宏命令后加封号所起的作用始终都是一样的。

举例如下:

#define foo(x)  bar(x); baz(x)
后面的程序可能会这样调用该宏命令:
foo(wolf);
编译器预处理为:
bar(wolf); baz(wolf);
这个是预期的编译预处理结果,但接下来如果这样调用宏命令:
if (!feral)
    foo(wolf);
该结果可能会并不是你所期许的那样:
if (!feral)
    bar(wolf);
baz(wolf);


写一个在所有情况下都正确地达到你期许目的的multistatement 宏是不可能的。离开do/while(0)结构,让宏定义表现的像一个函数是不可能的。


如果我们用do/while(0)重新定义上面的宏,
#define foo(x)  do { bar(x); baz(x); } while (0)
现在,该宏定义功能上等价于前者。do确保花括号里的内在逻辑得到执行,while(0)确认该逻辑得到执行并且只执行一次,同上调用,编译预处理结果为:
if (!feral)
    do { bar(wolf); baz(wolf); } while (0);
语义等价如下:
if (!feral) {
    bar(wolf);
    baz(wolf);
}
说到这里,你可能会困惑为什么在宏定义外加层花括号达不到目的?为什么还必须要 do/while(0)结构。
我们仅仅用花括号,定义如下:
#define foo(x)  { bar(x); baz(x); }
这样的定义对上面的调用是没有问题的,但如果调用如下:
if (!feral)
    foo(wolf);
else
    bin(wolf);
预编译结果为:
if (!feral) {
    bar(wolf);
    baz(wolf);
};
else
    bin(wolf);
这是语法错误。

综上所述,在Linux和其他代码库中,宏内的逻辑都包含在do/while(0)结构中,确保在调用代码不管如何使用分号和花括号,宏在程序中的表现总是相同的。

—————————————————————————————————————————————————————————————————————————————

update:


编译自:http://www.pixelstech.net/article/1350871981-Significance-and-use-of-do%7B-%7Dwhile%280%29-

  • Help define complex macro to avoid error
如上述
  • Avoid warning caused by macro

由于内核的不同体系架构约束,空的宏定义可能会经常使用,编译的时候,空的宏定义会产生warning,为了避免这种warning,可以使用do{...}while(0)来定义空的宏。

#define EMPTYMICRO do{}while(0)

  • Avoid using goto to control program workflow

在一些函数中,return语句前需要处理一些工作,如释放之前用malloc分配的空间。goto语句可能是一个简单的方法:

int foo()
{
    somestruct* ptr = malloc(...);
 
    dosomething...;
    if(error)
    {
        goto END;
    }
 
    dosomething...;
    if(error)
    {
        goto END;
    }
    dosomething...;
 
END:
    free(ptr);
    return 0;
 
}

由于包含goto语句的程序可读性较差,因此很多人并不推荐使用。因此,我们使用do{...}while(0)来实现上述函数的功能:

int foo()
{
 
    somestruct* ptr = malloc(...);
 
    do{
        dosomething...;
        if(error)
        {
            break;
        }
 
        dosomething...;
        if(error)
        {
            break;
        }
        dosomething...;
    }while(0);
 
    free(ptr);
    return 0;
 
}

这里我们使用do{...}while(0)来包含函数的主体部分,并使用break来代替goto

  • Define a single function block to complete complex operations
当需要实现一个复杂操作的函数并且不想产生一个新的函数时,使用do{...}while(0),并且在其中定义的新变量作用域不会超出do{...}while(0)结构,不用担心重名问题。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值