Previous Table of Contents Next


Chapter 7:
Macros and Miscellaneous Pitfalls

A bad macro can drive a good programmer mad. Imagine the frustration when an unsuspecting programmer codes:

x = 3;
y = cube( x + 1 );
z = 5 * double( x );

thinking that cube( x + 1 ) will produce the value 64 (43), only to find that y is set to 10; and thinking that 5 * double( x ) will produce the value 30, only to find that z is set to 18. The mystery becomes clear when the programmer examines the cube and double macro definitions and finds:

#define cube( x ) x*x*x
#define double( x ) x+x

Given these definitions, the compiler expands cube( x + 1) as:

x + 1*x + 1*x + 1

which, because in C the * operator binds more tightly than the + operator, is equivalent to:

x + ( 1 * x ) + ( 1 * x ) + 1

When x is 3, the value of this expression is:

3 + ( 1 * 3 ) + ( 1 * 3 ) + 1

or 10.

Similarly, the compiler expands 5 * double( x ) as:

5 * x+x

which is equivalent to:

( 5 * x ) + x

When x is 3, this expression evaluates to 18.

Macros aren’t “magic”—the compiler simply replaces a macro reference with expanded text according to the macro’s definition. This simple text expansion requires care that the context of a macro expansion doesn’t cause the resulting expression to have an unexpected meaning. You can avoid many problems with macros by following a simple rule: Put parentheses (or other explicit delimiters) around the macro text and around each macro argument within the macro text.

Following this rule, the cube and double macros can be defined as:

#define cube( x ) ( ( x ) * ( x ) * ( x ) )
#define double( x ) ( ( x ) + ( x ) )

The two assignment statements above will then expand to:

y = ( ( x + 1 ) * ( x + 1 ) * ( x + 1 ) )
z = 5 * ( ( x ) + ( x ) )

which will do what the programmer originally expected.

You may remember that some macros I presented in earlier chapters don’t have so many parentheses. For example, in Chapter 4 I defined the cpystr macro as:

#define cpystr(  target, source )\    strcpymax( target, source, target##_maxlen)

In this case, the expansion text is always delimited by the strcpymax function name and closing parenthesis, so there’s no need for parentheses around the entire text. The target and source arguments are delimited by the commas that separate function arguments. It still wouldn’t hurt to add additional parentheses as a matter of good macro programming habits, however.

Even with the protection of parentheses, a simple macro, such as double, can cause unexpected results. The second statement below is intended to increment x (to 4) and put double the new value (8) in y.

x = 3;
y = double( ++x );

What it actually does is increase x to 5 and set y to 9. This results from the expanded code:

y = ( ( ++x ) + ( ++x ) );

which evaluates the macro argument twice. In this case, the argument ++x has the side effect of incrementing x, and the expanded macro does this twice instead of once, as intended. You can avoid such problems by following another rule: Never pass an expression that has side effects as a macro argument.

This example also provides additional evidence that C’s ++ and — operators, which seem so simple and “innocent,” are often the culprits in causing unintended side effects. You may recall that in Chapter 6 I showed how ++ and — can cause problems in assignment statements. The unary increment and decrement operators themselves are not really to blame; rather, it’s the common C programming practice of embedding an increment or decrement operation within a larger expression. C programmers frequently code

next = ary[ ++i ];

instead of

++i;
next = ary[ i ];

Within simple array subscripts, using ++ or — is a safe and generally comprehensible technique. You must be careful, however, to use the correct pre- or post-increment alternative. In contrast, with separate statements to increment the index and reference the array, you can always use pre-increment (e.g., ++i) because the statement order makes clear whether you are incrementing the index before or after referencing the array. In general, I recommend the use of separate statements for incrementing and decrementing array indexes because the code layout more strongly expresses the sequence of operations. This is not typical C style, but then much of what’s considered “standard” C style stems more from habit and fashion than good programming practices.

Most problems I’ve seen in C programs stem from many C programmers’ attitude that a simple ++i statement by itself is somehow “wasteful” (of what, I’m not sure), and a way must be found to embed all increment and decrement operations into adjacent statements. It’s a pity for those C programmers who don’t follow the general guideline: Place simple increment and decrement operations in separate statements, because this guideline frees you from concerns about when ++, and — side effects can cause trouble and lets you use these otherwise nice syntactic elements of C. (For systems programming, a careful embedding of ++ or — may provide better performance in some cases. But in business programming, any potential advantages of such techniques are inconsequential and should not influence the way you use the ++ and — operators.)


Previous Table of Contents Next