423

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?

Henry Ecker
  • 34,399
  • 18
  • 41
  • 57
gilm
  • 7,690
  • 3
  • 41
  • 41
  • Have a look at [this question](http://stackoverflow.com/questions/243967/do-you-consider-this-technique-bad). – Federico A. Ramponi Nov 02 '08 at 21:38
  • 13
    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. – Doomsday May 18 '12 at 15:44
  • See "decrement_used_memory" of Redis line 53 [link]https://github.com/antirez/redis-tools/blob/master/zmalloc.c – yet Dec 09 '12 at 21:57
  • The duplicate is with the question marked as possible duplicate (first line of the post), not with the question given by Federico A. Ramponi. – Étienne May 02 '13 at 08:52
  • The suggested duplicate is only with respect to usage in macros. This question covers general usage. – dcow Nov 18 '15 at 04:41
  • 2
    other variants of this `do { ... } while ((void)0, 0)` used to silence compiler warnings about "constant condition". – Amro Jan 01 '16 at 00:56

5 Answers5

612

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
    ....
Greg Hewgill
  • 951,095
  • 183
  • 1,149
  • 1,285
  • 8
    Wouldn't `#define FOO(x) if(true){ foo(x); bar(x); } else void(0)` also work even though it's much uglier? – Adisak Aug 07 '13 at 22:39
  • 2
    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? – user10607 Nov 03 '14 at 19:42
  • 6
    @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. – Greg Hewgill Nov 03 '14 at 19:45
  • 2
    @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? – Aaron McDaid Jun 25 '15 at 13:04
  • 1
    @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: https://ideone.com/TdN4YG So again, my ugly macro works just as well as the do while macro. – Adisak Jun 26 '15 at 17:49
  • 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. – Adisak Jun 26 '15 at 17:50
  • Ah! Agreed @Adisak . 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. – Aaron McDaid Jun 27 '15 at 12:10
  • 23
    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... – LarryF Jul 16 '15 at 20:37
  • 74
    @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. – Greg Hewgill Jul 16 '15 at 23:39
  • The use or non-use of a semicolon doesn't seem to be making a difference for me? – Max Oct 28 '16 at 03:33
  • 1
    @Max 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. – Greg Hewgill Oct 28 '16 at 03:57
  • 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 semicolon – Max Oct 28 '16 at 04:05
  • @Max Like my example, you need to *not* use braces, *and* use an `else` clause. – Greg Hewgill Oct 28 '16 at 05:14
  • I use an else clause and even without braces, I am able to call the macro with or without a semicolon – Max Oct 28 '16 at 05:21
  • 2
    @Max ok, if you still have questions about what is going on for you, please ask a new question instead of using comments. Thanks. – Greg Hewgill Oct 28 '16 at 06:10
  • 1
    Anyone seeing this now may find this informative: https://gcc.gnu.org/onlinedocs/gcc-4.9.2/gcc/Statement-Exprs.html#Statement-Exprs "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);}) – abjoshi - Reinstate Monica Nov 28 '18 at 08:01
  • 1
    @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. – Marco Sulla Oct 31 '19 at 21:10
  • 2
    I don't understand why the braces don't work. – Aaron Franke Dec 05 '19 at 01:50
  • 5
    @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. – yonil Jun 26 '20 at 10:06
  • Actually the code would still work by enclosing the macro in braces solely without the `do { } while(0)` – Cristian F. Dec 28 '22 at 17:46
136

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);
nickf
  • 537,072
  • 198
  • 649
  • 721
Jere.Jones
  • 9,915
  • 5
  • 35
  • 38
  • 19
    er, factor it into a method, and use early return. – Dustin Getz Nov 02 '08 at 23:35
  • 16
    or... just use do { } while(0). – nickf Nov 02 '08 at 23:48
  • 85
    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. – FireFly Apr 23 '14 at 17:46
  • 1
    @FireFly Oh, like [`goto fail`](https://www.imperialviolet.org/2014/02/22/applebug.html)? (granted, `goto` wasn't the error, which actually makes it even funnier) – wchargin Jun 13 '14 at 05:11
  • 6
    @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. – Niccolo M. Jun 15 '14 at 11:15
  • 6
    @NiccoloM. "granted, goto wasn't the error, which actually makes it even funnier" – wchargin Jun 15 '14 at 17:36
  • 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. – Gastón Saillén Aug 07 '19 at 15:19
  • 2
    doesn't make it "funnier", just makes it a completely vacuous comment – Lightness Races in Orbit Dec 11 '19 at 14:47
112

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.

David Ferenczy Rogožan
  • 23,966
  • 9
  • 79
  • 68
Martin v. Löwis
  • 124,830
  • 17
  • 198
  • 235
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;
}
ubuntu-fanboy
  • 511
  • 5
  • 3
  • 8
    This is a GCC extension. In C++11 you could do the same thing with a lambda, though. – celticminstrel Jul 08 '15 at 06:32
  • 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,? – yonil Jun 26 '20 at 10:17
-7

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');
Daniel Spiewak
  • 54,515
  • 14
  • 108
  • 120
  • 1
    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. – Jon Davis Nov 02 '08 at 22:36
  • 23
    I wasn't the one to downvote; however, the question is very specific, and the answer is true in general but out of context. – tzot Nov 02 '08 at 23:04
  • 1
    Statement is correct but it is out of context. – Nadeem Khan Mar 18 '19 at 10:08