“{{}} while(0)”在内核代码中的作用是什么?

时间:2010-03-04 17:21:25

标签: c linux-kernel kernel

  

可能重复:
  What’s the use of do while(0) when we define a macro?
  Why are there sometimes meaningless do/while and if/else statements in C/C++ macros?
  C multi-line macro: do/while(0) vs scope block

我已经看过很多这样的用法,之前我认为程序员想要轻松打破一段代码。为什么我们需要在这里执行while {...} 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)

6 个答案:

答案 0 :(得分:25)

它总是在宏中使用,因此在调用后需要分号,就像调用常规函数一样。

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

__local_irq_save(1);

,而

__local_irq_save(1)

会导致错误的分号错误。如果不在那里,那就不会发生这种情况。如果它只是一个范围界定,一个简单的大括号对就足够了。

答案 1 :(得分:24)

它允许代码显示在这里:

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

// -> if(a) do { .. } while(0); else ...;

如果他们只是使用{ .. }你会得到

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

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

答案 2 :(得分:11)

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

由于这个属性,你可以使用do/while来编写多语句宏,它可以安全地用作“普通”函数而不用担心宏内部的内容,如下例所示

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

答案 3 :(得分:5)

已经给出了答案(因此宏在调用时强制;),但是我已经看到过这种语句的另一种用法:它允许在“循环”中的任何地方调用break,如果需要,提前终止。基本上是你的同事程序员不会谋杀你的“goto”。

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

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

答案 4 :(得分:2)

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

if (true)
{
    // Do stuff.
}

修改

我在你的例子中没有看到它,但是其中一个函数调用实际上可能是一个宏,在这种情况下,do / while(0)和if(true)之间存在一个关键区别,即前者允许continuebreak

答案 5 :(得分:2)

它将宏的行为用作真实的语句或函数调用。

语句是{ expression-list }expression;,因此在定义需要多个表达式的宏时会出现问题,因为如果使用{ },则会出现语法错误宏的调用者非常合理地在else之前添加;

if(whatever)
  f(x);
else
  f(y);

如果f()是一个单一的语句宏,那很好,但如果它是一个宏而且复杂的东西怎么办?您最终得到if(...) { s1; s2; }; else ...,但这不起作用。

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

do .. while(0)模式是“包裹构造”方法。