C 什么是;do{…}while(0)";你在内核代码中做什么?

C 什么是;do{…}while(0)";你在内核代码中做什么?,c,linux-kernel,kernel,C,Linux Kernel,Kernel,可能的重复项: 我见过很多这样的用法,以前我认为程序员想要轻松地突破代码块。为什么我们需要一个do{…}while(0)循环?我们想告诉编译器什么吗 例如,在Linux内核2.6.25中,包含/asm-ia64/system.h /* * - clearing psr.i is implicitly serialized (visible by next insn) * - setting psr.i requires data serialization * - we need a

可能的重复项:


我见过很多这样的用法,以前我认为程序员想要轻松地突破代码块。为什么我们需要一个do{…}while(0)循环?我们想告诉编译器什么吗

例如,在Linux内核2.6.25中,包含/asm-ia64/system.h

/*
 * - clearing psr.i is implicitly serialized (visible by next insn)
 * - setting psr.i requires data serialization
 * - we need a stop-bit before reading PSR because we sometimes
 *   write a floating-point register right before reading the PSR
 *   and that writes to PSR.mfl
 */
#define __local_irq_save(x)         \
do {                    \
    ia64_stop();                \
    (x) = ia64_getreg(_IA64_REG_PSR);   \
    ia64_stop();                \
    ia64_rsm(IA64_PSR_I);           \
} while (0)

看起来它只是用来确定范围的。它类似于:

if (true)
{
    // Do stuff.
}
编辑


我在您的示例中没有看到,但有可能其中一个函数调用实际上是一个宏,在这种情况下,do/while(0)和if(true)之间有一个关键区别,即前者允许
继续
中断
它总是在宏中使用,因此调用后需要分号,就像调用常规函数一样

在您的示例中,您必须编写

__local_irq_save(1);


将导致关于缺少分号的错误。如果没有do while,就不会发生这种情况。如果只是关于范围,一对简单的花括号就足够了。

它允许代码出现在这里:

if(a) __local_irq_save(x); else ...;

// -> if(a) do { .. } while(0); else ...;
如果他们只是使用一个
{..}
你会得到

if(a) { ... }; else ...; 

else将不再属于任何
if
,因为分号将是下一个语句,并将
else
与前面的
if
分开。将发生编译错误

do{…}while(0)
构造的目的是将一组语句转换为单个复合语句,可以用
终止。你看,在C语言中,
do/while
构造有一个奇怪而不寻常的特性:即使它作为复合语句“工作”,它也需要一个
在末尾。C中没有其他复合构造具有此属性

由于此属性,您可以使用
do/while
编写多语句宏,它可以安全地用作“普通”函数,而无需担心宏中的内容,如以下示例所示

if (/* some condition */)
  __local_irq_save(x); /* <- we can safely put `;` here */
else
  /* whatever */;
if(/*某些条件*/)

__本地irq保存(x);/* 答案已经给出了(因此宏在调用时强制使用
),但是我看到了这种语句的另一个用法:它允许在“循环”中的任何位置调用break,如果需要,可以提前终止。本质上是一个“goto”,你的程序员同事不会因为它而杀了你

do {
    int i = do_something();
    if(i == 0) { break; } // Skips the remainder of the logic
    do_something_else();
} while(0);

请注意,这仍然相当混乱,因此我不鼓励使用它。

它使宏的使用像真正的语句或函数调用一样。

语句是
{expression list}
表达式{}
,那么如果宏的调用者合理地添加
,就会出现语法错误在else之前

if(whatever)
  f(x);
else
  f(y);
如果
f()
是一个单语句宏,很好,但是如果它是一个宏和一些复杂的东西呢?如果(…){s1;s2;};否则…
这就不行了

因此,宏的编写者必须将其转换为实函数,将构造封装在单个语句中,或者使用gnu扩展


do。。而(0)
模式是“包装构造”方法。

+1这是一个更好的解释。这是一个方便的把戏!有点迷人。不过,我建议在这样的宏上随时使用内联函数(如果您使用的是C++),这样可以免费获得分号要求:@OregonGhost:当然,这是来自Linux内核的。你有没有注意到Linus Torvalds对C++的看法?我想说,如果没有<代码>如果例子,它并不能真正解释那个问题>代码>;代码>。我们可以使用一个普通的
{}
将成为可选的。那又怎么样?如果我们忘了放
不需要的地方?但是,一旦你看到了
if
的问题,就很清楚
do/while
的技巧是如何更好。如果只是为了范围界定,为什么还要使用“do”和“while(0)”或者在这个答案中使用“if(true)”呢?为什么不让开头和结尾的花括号独立起来呢?答案是,这不仅仅是为了范围界定。安德烈和其他人解释了其余的部分,这也是一个合理的理由。另一方面,我总是对控制流语句使用大括号,这样就不会发生这种情况。因此,编译器错误对我来说是可以接受的。如果else与if分离而没有编译错误,则情况会更糟;)主持人:并不总是有语法错误<代码>如果(允许)如果(!适当)\设置\错误(现在不是);否则(赢);
时,如果只使用
{…}
则else实际附加到
if(allowed)
而不是
if(!applicate)
,则此操作有效。迈克尔·斯佩尔:不是真的。如果
\u set\u error
使用
{}
,则
-s,则
之后的代码设置错误
将终止
,否则
将成为孤立-语法错误。我听说了与mis关联的
else
的潜在问题,但我不能给出一个合适的例子。也许这真的不可能,只是一个城市传奇。有人吗?@AndreyT,谢谢你纠正他。我不完全确定,也没有测试它。但这是有道理的。事实上,这就是标准的表述方式:“在if语句的第二种形式(包括else)中,如果第一个子语句也是if语句,那么内部if语句将包含else部分。”@Michael Speer:你是个白痴@安德烈:谢谢你的更正。我不知道为什么在我打字的时候这对我来说是有意义的。这是你的答案:我认为你是对的。那"创造"
if(whatever)
  f(x);
else
  f(y);