Tricky Tricks ›› Programming Tricks ›› C Languages Tricks ›› More On Preprocessor Macros

More On Preprocessor Macros

C Programming Tricks

More on preprocessor macros:

C preprocessor macros can be used with great benefits; but you have to know how to write them, or you can run into unexpected problems. For example, let's look at the following innocent-looking #define:

 /* WRONG! */
 #define SQUARE(x)   x * x
 

Does it work? Most of the time. But when someone (not necessarily the author of the macro) attempts to calculate SQUARE(3 - 1), he or she gets -1. Surprising, isn't it? The problem is especially hard to trace if the macro call is hidden in a large expression.

These problems arise because C macros are expanded at the preprocessor level, and all syntax and semantic analysis is done after this stage. Preproccesor simply replaces SQUARE(3 - 1) with 3 - 1 * 3 - 1, which is, of course, parsed as 3 - (1*3) - 1 and evaluated to -1. This bug can be fixed by puting parentheses around all references to arguments in a macro definition; but that still leaves some problems unsolved. Let's look at a bit different macro:

 /* STILL WRONG! */
 #define DOUBLE(x)   (x) + (x)
 

Again, this works sometimes and misteriously gives incorrect results in other expressions. For example, DOUBLE(2)*10 gives an unexpected result, because precedence of multiplication is higher than that of addition. This can be fixed by enclosing the macro body in parentheses:

 /* Sort of right, but see below.. */
 #define DOUBLE(x)   ((x) + (x))
 

This is the best we can do as far as preprocessor macros go. But there are still pitfalls: x is evaluated twice, so if an argument to a macro has any side effects (for example, ++ or --, or a call to a function), then again the Wrong Things will happen. This can't be avoided, and therefore this behaviour is officially declared a Feature. BTW, the convention to use upper case letters for macro names is a good thing because it alerts the programmer to potential problems.

Morale: write macros defensively, puting parentheses around arguments and macro body; do not pass expressions with side effects to macros; use alternatives (such as inline functions) whenever possible.

Partners