I wrote previously about the strange habit some developers have of using do...while(0) to avoid using gotos. After a recent conversation with my co-worker Cory, I learned do...while(0) blocks are even more useful.
C macros operate like direct textual replacements, so when the compiler sees the string "swap( x, y )" it replaces it with whatever value it had for the macro, producing an if...else clause like this:
And this is where the do { ... } while( 0 ) comes in handy. If we define our swap macro like this, we'll get the correct behavior in if's, for's and as just plain statements:
Cheers!
Consider the situation where you want to have a C macro that swaps two values. Here's a naive implementation that swaps two integers:
#define swap( a, b ) int temp = a; a = b; b = temp;And you might use it like this:
int x = 12;And since you executed the swap after assigning values to x & y and before printing, you would expect this fragment to print out something like this:
int y = 17;
swap( x, y );
printf( "(x,y) = (%d, %d)\n", x, y );
(x,y) = (17,12)But there are problems with this macro. Let's say we want to use it in an if...else clause, like this:
int accumulator = 2317;If you tried something like this, you would probably get an error telling you the compiler found an else unrelated to a previous if. And here's why...
int count = 0;
/* do some stuff */
if( accumulator > 1000 )
swap( x, y );
else
count++;
C macros operate like direct textual replacements, so when the compiler sees the string "swap( x, y )" it replaces it with whatever value it had for the macro, producing an if...else clause like this:
if( accumulator > 1000 )The syntax of the C language says that you get either a curly-brace enclosed block or a single statement following an if (). The problem here is that macro substitution has inserted multiple statements (that are not part of a curly-brace enclosed block) after the if. You could define the swap macro like this:
int temp = x;
x = y;
y = temp;;
else
count++;
#define swap( a, b ) { int temp = a; a = b; b = temp; }But this won't work outside an if statement.
And this is where the do { ... } while( 0 ) comes in handy. If we define our swap macro like this, we'll get the correct behavior in if's, for's and as just plain statements:
#define swap( a, b ) do { int temp = a; a = b; b = temp; } while ( 0 )Try it yourself; here's a quick program that defines two macros: swap and swap_naive. The former "does the right thing" while the latter doesn't. The program accepts a command line option, converts it to an integer and swaps different variable depending on whether the option is greater than 50. This program won't compile until you replace the "swap_naive" macro calls with plain ol' "swap" calls:
#include <stdio.h><stdlib.h> 
#include
#define swap_naive( a, b ) temp = a; a = b; b = temp;
#define swap( a, b ) do { int t = a; a = b; b = t; } while ( 0 )
void main( int argc, char *argv[] ) {
int x = 17;
int y = 19;
int z = 23;
int temp;
int c;
if( argc > 1 ) {
c = atoi( argv[1] );
printf( "comparison value lifted from command line: %d\n", c );
} else {
c = 12;
printf( "using default comparison value: 12\n" );
}
printf( "x, y and z before swap: x = %d, y = %d, z = %d\n", x, y, z );
if( c > 50 )
swap_naive( x, y );
else
swap_naive( y, z );
printf( "after swap: x = %d, y = %d, z = %d\n", x, y, z );
}
Cheers!
 
