开发者

What does "do { ... } while (0)" do exactly in kernel code? [duplicate]

开发者 https://www.devze.com 2022-12-21 19:24 出处:网络
This question already has answers here: Closed 12 years ago. Possible Duplicates: What’s the use of do while(0) when we define a macro?
This question already has answers here: Closed 12 years ago.

Possible Duplicates:

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

I have seen a lot of usages like this, previously I though that the programmer wanted to break out of a block of code easily. Why do we need a do { ... } while (0) loop here? Are we trying to tell the compiler something?

For instance in Linux kernel 2.6.25, include/asm-ia64/system.h

/*
 * - clearing psr.i is implicitly serialized (visible by next insn)
 * - set开发者_如何转开发ting 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)


It's always used in macros so that a semicolon is required after a call, just like when calling a regular function.

In your example, you have to write

__local_irq_save(1);

while

__local_irq_save(1)

would result in an error about a missing semicolon. This would not happen if the do while was not there. If it was just about scoping, a simple curly brace pair would suffice.


It allows for the code to appear here:

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

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

If they simply used a { .. } you would get

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

The else would not belong to any if anymore, because the semicolon would be the next statement and separate the else from the preceeding if. A compile error would occur.


The purpose of do{ ... } while(0) construct is to turn a group of statements into a single compound statement that can be terminated with a ;. You see, in C language the do/while construct has one weird and unusual property: even though it "works" as a compound statement, it expects a ; at the end. No other compound constructs in C have this property.

Because of this property, you can use do/while to write multi-statement macros, which can be safely used as "ordinary" functions without worrying what's inside the macro, as in the following example

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


The answer has already been given (so the macro forces a ; when called), but another use of this kind of statement that I have seen: it allows break to be called anywhere in the "loop", early terminating if needed. Essentially a "goto" that your fellow programmers wouldn't murder you for.

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

Note that this is still fairly confusing, so I don't encourage its use.


Looks like it's there just for scoping. It's similar to:

if (true)
{
    // Do stuff.
}

edit

I don't see it in your example, but it's possible that one of those function calls is actually a macro, in which case there's one key difference between do/while(0) and if(true), which is that the former allows continue and break.


It makes use of the macro act like a real statement or function call.

A statement is either { expression-list } or expression; so that poses a problem when defining macros that need more than one expression, because if you use { } then a syntax error will occur if the caller of the macro quite reasonably adds a ; before an else.

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

If f() is a single statement macro, fine, but what if it's a macro and something complicated? You end up with if(...) { s1; s2; }; else ... and that doesn't work.

So the writer of the macro has to then either make it into a real function, wrap the construct in a single statement, or use a gnu extension.

The do .. while(0) pattern is the "wrap the construct" approach.

0

精彩评论

暂无评论...
验证码 换一张
取 消