| Swallowing the Semicolon

Previous | Macro Pitfalls | Next

Often it is desirable to define a macro that expands into a compound statement. Consider, for example, the following macro, that advances a pointer (the argument `p` says where to find it) across whitespace characters:

    #define SKIP_SPACES(p, limit)  \
    { char *lim = (limit);         \
      while (p < lim) {            \
        if (*p++ != ' ') {         \
          p--; break; }}}

Here backslash-newline is used to split the macro definition, which must be a single logical line, so that it resembles the way such code would be laid out if not part of a macro definition.
A call to this macro might be `SKIP_SPACES (p, lim)`. Strictly speaking, the call expands to a compound statement, which is a complete statement with no need for a semicolon to end it. However, since it looks like a function call, it minimizes confusion if you can use it like a function call, writing a semicolon afterward, as in `SKIP_SPACES (p, lim);`
This can cause trouble before `else` statements, because the semicolon is actually a null statement. Suppose you write

    if (*p != 0)
      SKIP_SPACES (p, lim);
    else ...

The presence of two statements - the compound statement and a null statement - in between the `if` condition and the `else` makes invalid C code.
The definition of the macro `SKIP_SPACES` can be altered to solve this problem, using a `do ... while` statement. Here is how:

    #define SKIP_SPACES(p, limit)     \
    do { char *lim = (limit);         \
         while (p < lim) {            \
           if (*p++ != ' ') {         \
             p--; break; }}}          \
    while (0)

Now `SKIP_SPACES (p, lim);` expands into

    do {...} while (0);

which is one statement. The loop executes exactly once; most compilers generate no extra code for it.