do { ... } while (0) — what is it good for? [duplicate]
Asked Answered
M

5

437

I've been seeing that expression for over 10 years now. I've been trying to think what it's good for. Since I see it mostly in #defines, I assume it's good for inner scope variable declaration and for using breaks (instead of gotos.)

Is it good for anything else? Do you use it?

Mildew answered 2/11, 2008 at 21:36 Comment(6)
Have a look at this question.Delphina
Actually, it is not a duplicate since the linked q/a is not specific to define. It's easy to compare both answers to state it's not a duplicate.Ebbarta
See "decrement_used_memory" of Redis line 53 [link]github.com/antirez/redis-tools/blob/master/zmalloc.cFaris
The duplicate is with the question marked as possible duplicate (first line of the post), not with the question given by Federico A. Ramponi.Choking
The suggested duplicate is only with respect to usage in macros. This question covers general usage.Soursop
other variants of this do { ... } while ((void)0, 0) used to silence compiler warnings about "constant condition".Liz
M
625

It's the only construct in C that you can use to #define a multistatement operation, put a semicolon after, and still use within an if statement. An example might help:

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

if (condition)
    FOO(x);
else // syntax error here
    ...;

Even using braces doesn't help:

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

Using this in an if statement would require that you omit the semicolon, which is counterintuitive:

if (condition)
    FOO(x)
else
    ...

If you define FOO like this:

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

then the following is syntactically correct:

if (condition)
    FOO(x);
else
    ....
Marilynnmarimba answered 2/11, 2008 at 21:40 Comment(21)
Wouldn't #define FOO(x) if(true){ foo(x); bar(x); } else void(0) also work even though it's much uglier?Sotelo
But how about #define FOO(x) foo(x), bar(x) so using a comma instead of a semicolon. Would not this work fine in an if else?Converted
@user10607: Yes, that works if your macro expansions is a list of expressions. However, if you want to include an if or while inside the expansion, that trick won't work either.Marilynnmarimba
@Adisak, I think that this would break your example: FOO(x) , bar(); because the ,bar() would be absorbed into void(0),bar(). i.e. the comma operator would be applied at the wrong place, leading to strange behaviour. do { ... } while(0) is very well established, does what it's supposed to do. Most importantly, when experienced developers see do { ... } while(0) written by others, they can see exactly what is going on. Why use anything else, which would only worry experienced developers, even if it might be valid?Wrapper
@AaronMcDaid Well, the comma operator does screw things up as you mention. But it also screws up the do-while case. This is not valid C++ 'do {} while(0), printf("hello world\n");' either. I point you to this test: ideone.com/TdN4YG So again, my ugly macro works just as well as the do while macro.Sotelo
Also, I never said that my ugly macro was better than the do-while version. In fact, I prefer the do-while. I was just refuting the assertion in the answer that do-while is the ONLY way to do this.Sotelo
Ah! Agreed @Sotelo . That is interesting. I guess do {...} while(0) is the conventional way, and therefore it's best to be consistent and stick with that, unless the alternative does something that do {...} while(0) cannot.Wrapper
I still think this is UGLY, and SLOPPY coding, and CAN be avoided, if coded correctly. this goes back to some of the common sense C rules, of ALWAYS using curly's on your IF statements, even if you only have ONE operation after the IF. This should be standard practice, IMVHO...Kippar
@LarryF: The problem is, if you're writing a header file for use by others, you don't get to choose how other people are going to use your macro. In order to avoid unexpected surprises inflicted upon your users, you have to use a technique like the above to make your macro behave as any other C statement would behave.Marilynnmarimba
The use or non-use of a semicolon doesn't seem to be making a difference for me?Cursor
@Cursor Are you using your macro in an if statement with no braces like in my first example? In most other situations the semicolon doesn't matter as much.Marilynnmarimba
yes. I'm doing if(true) { FOO(x); } and if(true) { FOO(x) }, semicolon and no-semicolon makes no difference without do while() but the case that uses do while() doesn't compile without semicolonCursor
@Cursor Like my example, you need to not use braces, and use an else clause.Marilynnmarimba
I use an else clause and even without braces, I am able to call the macro with or without a semicolonCursor
@Cursor ok, if you still have questions about what is going on for you, please ask a new question instead of using comments. Thanks.Marilynnmarimba
Anyone seeing this now may find this informative: gcc.gnu.org/onlinedocs/gcc-4.9.2/gcc/… "A compound statement enclosed in parentheses may appear as an expression in GNU C. This allows you to use loops, switches, and local variables within an expression." . So we can just use #define FOO(x) ({foo(x); bar(x);})Thurgau
@GregHewgill: sorry but I completely agree with LarryF: if you are so lazy that you don't put braces when the expression is only one, well, you deserve my code doesn't work for you. Not mentioning that not putting braces is always a bad idea, since if you add another statement after the if without braces, maybe you think it will be executed only if you enter the if, but it's not.Encompass
I don't understand why the braces don't work.Cholecystectomy
@AaronFranke because {...}; is actually made of two statements, the {...} part and another empty statement after it. do {...} while(0); is one statement, though. Larry/Marco when users write FOO(a, b, c) they expect it (unless explicitly intended otherwise) to result in an expression, much like a call to a void-returning function. Yours is actually the sloppy approach. The code style convention of your user should be none of your concern.Wickham
Actually the code would still work by enclosing the macro in braces solely without the do { } while(0)Hexamerous
Are there static analysis tools that can find this pattern? It's more of a problem than I expected in production code.Hawsepiece
G
140

It is a way to simplify error checking and avoid deep nested if's. For example:

do {
  // do something
  if (error) {
    break;
  }
  // do something else
  if (error) {
    break;
  }
  // etc..
} while (0);
Germanium answered 2/11, 2008 at 21:38 Comment(8)
er, factor it into a method, and use early return.Guyer
or... just use do { } while(0).Feune
Or use goto. No, seriously, if (error) goto error; and a error: ... near the end seems like a cleaner version that accomplishes the same thing, to me.Housecoat
@Housecoat Oh, like goto fail? (granted, goto wasn't the error, which actually makes it even funnier)Crinum
@WChargin: the "goto fail" code in the article you linked too would have failed with a "break" too. Somebody just duplicated a line there. It wasn't goto's fault.Instinctive
@NiccoloM. "granted, goto wasn't the error, which actually makes it even funnier"Crinum
I would recommend using this when handling roots of a function, since you dont really know how many times you would need to loop a function to be close to 0 and encounter the root, and this loop can guarantee an optimal loop time to find the nearest 0.000... approach.Pain
doesn't make it "funnier", just makes it a completely vacuous commentMonique
B
115

It helps to group multiple statements into a single one so that a function-like macro can actually be used as a function. Suppose you have:

#define FOO(n)   foo(n);bar(n)

and you do:

void foobar(int n) {
  if (n)
     FOO(n);
}

then this expands to:

void foobar(int n) {
  if (n)
     foo(n);bar(n);
}

Notice that the second call bar(n) is not part of the if statement anymore.

Wrap both into do { } while(0), and you can also use the macro in an if statement.

Bootlick answered 2/11, 2008 at 21:39 Comment(4)
Very clear answer. +1Nader
Clearly understood. ;)Soane
Why not just put braces around it? #define FOO(n) {foo(n);bar(n)}Endomorph
@Endomorph Because #define FOO(n) {foo(n);bar(n)} would expand to {foo(n);bar(n)}. Notice that there is no semicolon after bar(n).Hibachi
H
23

It is interesting to note the following situation where the do {} while (0) loop won't work for you:

If you want a function-like macro that returns a value, then you will need a statement expression: ({stmt; stmt;}) instead of do {} while(0):


#include <stdio.h>

#define log_to_string1(str, fmt, arg...) \
    do { \
        sprintf(str, "%s: " fmt, "myprog", ##arg); \
    } while (0)

#define log_to_string2(str, fmt, arg...) \
    ({ \
        sprintf(str, "%s: " fmt, "myprog", ##arg); \
    })

int main() {
        char buf[1000];
        int n = 0;

        log_to_string1(buf, "%s\n", "No assignment, OK");

        n += log_to_string1(buf + n, "%s\n", "NOT OK: gcc: error: expected expression before 'do'");

        n += log_to_string2(buf + n, "%s\n", "This fixes it");
        n += log_to_string2(buf + n, "%s\n", "Assignment worked!");
        printf("%s", buf);
        return 0;
}
Hemline answered 7/1, 2011 at 16:34 Comment(2)
This is a GCC extension. In C++11 you could do the same thing with a lambda, though.Toleration
TIL statement expressions. Neat. How does it look with a lambda? I'm curious. A lambda returns a functor, essentially, you still have to call it, then return the result from the macro. both the lambda and the call need to form a single statement. How do you do it? operator,?Wickham
N
-8

Generically, do/while is good for any sort of loop construct where one must execute the loop at least once. It is possible to emulate this sort of looping through either a straight while or even a for loop, but often the result is a little less elegant. I'll admit that specific applications of this pattern are fairly rare, but they do exist. One which springs to mind is a menu-based console application:

do {
    char c = read_input();

    process_input(c);
} while (c != 'Q');
Nata answered 2/11, 2008 at 21:39 Comment(3)
It's available in C#, too, which doesn't have macros. I'm not sure why someone down-voted this reply but I see it as the almost-right answer, except it overlooked the explicit "0" in the while condition. Please, people, if you down-vote someone's reply please comment.Oilla
I wasn't the one to downvote; however, the question is very specific, and the answer is true in general but out of context.Novocaine
Statement is correct but it is out of context.Bio

© 2022 - 2024 — McMap. All rights reserved.