Danny's Lab

Engineering the World

Multi-Line Macros in C

Published on: Jun 16, 2010
Reading time: 1 minute

Every once in a while you need to use a multi-line macro in C. Typically this is because you're doing something like inlining, but for whatever reason inlining isn't particle/portable. I'll first give you the template for doing this correctly, then explain why.

Let's say you want to have a macro run\_and\_test() and you want it to execute run() and test(). The correct form then is this:

#define run_and_test()   do { run(); test(); } while(0)  /* Note: no ending semi-colon */

Explaination

The beginning C student may start out with something like this:

#define run_and_test()   run(); test();

But clearly there's a problem here when used for example in a while loop. A line like this:

while( x-- ) run_and_test()

Will be expanded to:

while(x--) run(); test();

Clearly this is not what we want.

The next thing you might think is to put braces around like so:

#define run_and_test()   { run(); test(); }

But this now has a problem in the case of nested if's:

if( condition1 ) if ( condition2 ) run_and_test(); else something2();

Everything appears okay, doesn't it? However, upon closer inspection, the semi-colon will cause some unintended consequences. The above code expands to:

if( condition1 ) if( condition2 ) { run(); test(); }; /* this semi-colon breaks things*/ else something2();

While normally, empty statements in C are valid, the semi-colon after run_and_test(); will now actually be a syntax error. You might argue that you should just use run_and_test() without a semi-colon. Perhaps even put it in all caps, like RUN_AND_TEST() to ensure the user is aware of this requirement. However, this special condition is difficult for developers specifically because it is very "un-C-like".

The thing that solves all these conditions, however, is to place the multiple lines in a do { } while(0) block as shown above. The while(0) ensures the statement gets executed only once. However, the lack of the semi-colon enforces the typical usage pattern by the developer.

Depending on your macro, the developer may still have to know it's a macro, as there may be side effects, especially if your macro supports arguments. But at least this is one less thing he has to be aware of.