12

Possible Duplicates:
Why are there sometimes meaningless do/while and if/else statements in C/C++ macros?
do { … } while (0) what is it good for?

I'm working on some C code filled with macros like this:

#define SAFE_FREE(x) do { if ((x) != NULL) {free(x); x=NULL;} } while(0)

Can anyone explain what this macro does, and why do {} while(0) is needed? Wouldn't that just execute the code once?

Community
  • 1
  • 1
igul222
  • 8,557
  • 14
  • 52
  • 60

4 Answers4

23
do { stuff() } while(0);

is doing the exact same thing as stuff(). So what's the big deal, then? The issue is with the syntax of macros. Suppose we defined the macro like:

#define SAFE_FREE(x) if ((x) != NULL) { free(x); x=NULL; }

Then, there are two issue. The first is relatively minor: uses of SAFE_FREE no longer require a trailing semi-colon. More importantly, though, code like:

if (...)
  SAFE_FREE(x)
else
   stuff();

Will expand to:

if (...)
  if ((x) != NULL) {
    free(x);
    x = NULL;
  } else
    stuff();

Note, how the else now matches the wrong if statement. This completely alters the program-flow, even though it does not look like that in the source code.

Defining the macro as above prevents weird behavior as above, since do { ... } while(0) acts just like a statement without its semicolon.

sebastian
  • 35
  • 6
jade
  • 744
  • 5
  • 16
  • 1
    W.r.t "I don't know if any like MSVC, gcc, icc, lcc, etc. remove the loop like they ought to, but optimization is surely a difficult problem to solve when it comes to compiling code." You don't even need a particularly good compiler. It's such a common construct that GCC optimizes it out even with optimizations turned off. It's not even an optimization-- something to make the code run faster-- per se: just a trivial elimination of unnecessary syntax. – jade Apr 22 '10 at 01:09
  • gcc does not optimize this out with optimizations turned off. gcc doesn't do anything that restructures your code when optimizations are turned off, otherwise debugging would be a nightmare. – Jason Coco Apr 22 '10 at 01:17
  • 2
    @Jason Coo: um... yes it does. Verified a simple example both with and without the "do { ...} while(0);" wrapper, with -O0 and the default options. gcc gives me the exact same output. (4.3.2, Debian 4.3.2-1.1, on Debian lenny.) – jade Apr 22 '10 at 01:45
  • 1
    Why not use just a block? It seems the do while(0) is still redundant and could be accomplished with a set of curly braces as shown in the above code. The semicolon is the only reason for the while that I can see. – Billy ONeal Apr 22 '10 at 01:47
  • @Jason how would debugging become a nightmare? – Johannes Schaub - litb Apr 23 '10 at 04:07
15

The do while is a common convention which makes the macro require a trailing semi colon like a standard c function would. Other than that it just ensures the variable that has been freed is set to NULL so that any future calls to free it will not cause errors.

Nick Van Brunt
  • 15,244
  • 11
  • 66
  • 92
  • Wow, now that you explain it that way, it makes perfect sense. – Daniel A. White Apr 22 '10 at 01:00
  • Excellent. I've seen the `do {} while(0)` bit before but never understood why. – Brandon Bodnar Apr 22 '10 at 01:03
  • It not only makes sense, but if you have a really good compiler, it should optimize the loop out. I don't know if any like MSVC, gcc, icc, lcc, etc. remove the loop like they ought to, but optimization is surely a difficult problem to solve when it comes to compiling code. :P – Dustin Apr 22 '10 at 01:04
  • 1
    The main point about do/while(0) is to avoid expanded code that is very different the programmer's intention. – janm Apr 22 '10 at 01:09
  • 1
    @janm That can be accomplished by just using braces without needing the useless looking do/while(0). For example `#define SAFE_FREE(x) { if ((x) != NULL) {free(x); x=NULL;} }` – Brandon Bodnar Apr 22 '10 at 01:14
  • 1
    @Brandon Bodnár: No. Consider "if (test) SAFE_FREE(x); else blah();" Instant syntax error. – janm Apr 22 '10 at 04:08
  • @BrandonBodnar putting parenthesis around a code block `({ /* ... */ })` gets parsed as a single expression on most compilers these days, but it's nonstandard in ANSI C (I think C90 fixed this) – yyny Mar 02 '16 at 23:04
  • EDIT: C90 doesn't fix this :c – yyny Mar 02 '16 at 23:11
2

BTW On the C++ Style and Technique FAQ Bjarne Stroustrup suggests using an inline (template) function to do a "delete and null"

template<class T> inline void destroy(T*& p) { delete p; p = 0; } 
hamishmcn
  • 7,843
  • 10
  • 41
  • 46
1

The idea behind the do/while(0) is that you can use the macro where you would use a function call without unexpected errors.

For example, if you had code like:

if (today_is_tuesday())
    SAFE_FREE(x);
else
    eat_lunch();

and the macro was just:

#define SAFE_FREE(x)  if (x) { free(x); x = 0; }

You would get a very different result. The do/while convention avoids those errors by making it behave consistently.

janm
  • 17,976
  • 1
  • 43
  • 61