Today's Question:  What does your personal desk look like?        GIVE A SHOUT

do {...} while (0) in macros

  sonic0002        2014-01-23 07:16:13       139,163        14    

If you are a C programmer, you must be familiar with macros. They are powerful and can help you ease your work if used correctly. However, if you don't define macros carefully, they may bite you and drive you crazy. In many C programs, you may see a special macro definition which may seem not so straightforward. Here is one example:

#define __set_task_state(tsk, state_value)      \
    do { (tsk)->state = (state_value); } while (0)

There are many this kind of macros which uses do{...}while(0) in Linux kernels and other popular C libraries. What's the use of this macro? Robert Love from Google(previously worked on Linux kernel development) gives us the answer.

do{...}while(0) is the only construct in C that lets you define macros that always work the same way, so that a semicolon after your macro always has the same effect, regardless of how the macro is used (with particularly emphasis on the issue of nesting the macro in an if without curly-brackets). For example:

#define foo(x)  bar(x); baz(x)

Later you may call:

foo(wolf);

This will be expanded to:

bar(wolf); baz(wolf);

This is the expected output. Next let's see if we have:

if (!feral)
    foo(wolf);

The expansion may not be what you expect. The expansion may be:

if (!feral)
    bar(wolf);
baz(wolf);

It isn't possible to write multistatement macros that do the right thing in all situations. You can't make macros behave like functions—without do/while(0).

If we redefine the macro with do{...}while(0), we will see:

#define foo(x)  do { bar(x); baz(x); } while (0)

Now, this statement is functionally equivalent to the former. The do ensures the logic inside the curly-brackets executes, the while(0) ensures that happens but once. Same as without the loop.For the above if statement, it will be :

if (!feral)
    do { bar(wolf); baz(wolf); } while (0);

Semantically, it's the same as:

if (!feral) {
    bar(wolf);
    baz(wolf);
}

You might rejoin, why not just wrap the macro in curly-brackets? Why also have the do/while(0) logic? For example, we define the macro with curly bracket:

#define foo(x)  { bar(x); baz(x); }

This is fine for the above if statement, but if we have below statement:

if (!feral)
    foo(wolf);
else
    bin(wolf);

The expanded code will be :

if (!feral) {
    bar(wolf);
    baz(wolf);
};
else
    bin(wolf);

This is a syntax error.

In conclusion, macros in Linux and other codebases wrap their logic in do/while(0) because it ensures the macro always behaves the same, regardless of how semicolons and curly-brackets are used in the invoking code.

C  C++  MACRO 

Share on Facebook  Share on Twitter  Share on Weibo  Share on Reddit 

  RELATED


  14 COMMENTS


LifeH2O [Reply]@ 2016-11-29 12:31:41

Now I get it. Thanks for this simplistic explanation.

bart9h [Reply]@ 2016-11-29 16:37:46

It's useful not only for multi-statement macros, but also when you need to declare a variable inside the macro, as the variable will be limited to the do{}while scope.

Anonymous [Reply]@ 2016-11-29 16:49:55

What's the problem with this?

#define __set_task_state(tsk, state_value)\

    ((tsk)->state = (state_value))

 

Anonymous [Reply]@ 2016-11-29 19:47:41

__set_task_state has "__" in the name. It is a reserved symbol.

Anonymous [Reply]@ 2016-11-29 23:18:48

Does that generate excess assembly, or does the loop usually get optimized out?

LinAGKar [Reply]@ 2016-11-30 03:31:51

Any decent optimizing compiler should optimize it out.

Olivier [Reply]@ 2016-11-30 03:20:21
The compiler are smart enough to remove the do/while logic, the generated assembly code is lean.
Anonymous [Reply]@ 2016-11-30 10:04:28

Are there other examples where this would be useful, other than an if/else statement?  I'm used to writing if/else with braces:

if (!feral) {
    foo(wolf);
} else {
    bin(wolf);

 

}

which would prevent you from running into this pitfall.

Anonymous [Reply]@ 2016-11-30 19:13:12

In regards to "preventing you from running into this pitfall".

 

The problem is that even if you write the macro, you can't guarantee that your macro will be used correctly by other programmers in 100% of cases.  So the choice was/is made that by creating the do-block it works in braceless ifs.

Anonymous [Reply]@ 2023-01-10 19:09:50

Yes,I agree.I think that we can use macro function as a real "function" by using do,while.Generally, if only one statement in the block , there is no need to add "{}" .

Anonymous [Reply]@ 2016-12-01 11:58:56

#define foo(x) ( bar(x) , baz(x) )

Mohammad amin [Reply]@ 2017-11-23 03:55:44

Thank you very much for your good explanation 🌹

Hirrolot [Reply]@ 2020-08-22 15:56:04

It's not completely true, since we have `_Static_assert` that let macros behave in the same manner:

 

```

#define MY_MACRO(...) { __VA_ARGS__ } _Static_assert(1, "")

```

 

What is more, this technique lets ourselves define macros outside functions, unlike do-while.

0xmarcin [Reply]@ 2020-08-23 02:56:43

On the other hand do ... while(0) is not without cons. For example because this is a loop you can use break and continue instructions within it.