This article continues the one published two weeks ago: Top 101 mistakes done by novice programmers C


6. Macro expansion

All the mistakes in the Macro expansion are very sneaky, because they don't return any warning also with newer compiler.


6.b Dangling Else Error in Macros Containing If


An error very similar to the previous error can occur if a macro contains an if statement with no else clause. Consider the code fragment:

#define test(x) if(x) print(x)

if(...)

test(3);

else

...

The error here is that the else clause immediately after the call to test() will match the inner if, which in this case is the if in the replacement text for test(). The code expands out to become equivalent to:

if(...)

    if(3)

        print(3)

    else

        ...

where the else clause has matched the wrong if statement. As with the error with multiple statement macros, the programmer can prevent the error by using braces



6.c Side Effects to Macro Calls

This is a dangerous error that can occur when parameterized macros are used to imitate function calls. When side effects appear in the arguments to a macro, these side effects may occur more than once, or even not at all. For example, in the code fragment

#define sqr(x) (x * x)

...

y=sqr(i++);

the macro call expands out to "i++ * i++", where i is incremented twice. This is different to the expected result for function calls —side effects to function arguments are executed exactly once.



6.d Operator Precedence Errors

Another potential error created by the difference between function calls and macro calls involves operator precedence. There are two forms of this error. The first form applies to both parameterized and non-parameterized macros, and relates to the entire replacement text. The second form relates to the substitution of arguments for parameters of parameterized macros.

An example of the first form appears below:

#define twice(x) x+x

...

y= 3 * twice(2);

The assignment statement expands out to become "y= 3 * 2+2;". The ’*’ operator takes precedence over the ’+’ operator and y is assigned the incorrect value of 8.




6.e Operator Precedence Errors — Macro Parameters

The second form of operator precedence error occurs only in parameterized macros, and relates to the substitution of macro arguments. Consider the example below:

#define sqr(x) (x*x)

...

y= sqr(3+2);

The assignment to y expands out to "(3+2*3+2)" and again ’*’ takes precedence over ’+’, with the expression evaluating to 11 instead of the correct value of 25. The solution to this error is to place a pair of brackets around every occurrence of a macro parameter name in the replacement text (Kernighan and Ritchie, 1988). Hence, the corrected sqr macro becomes:

#define sqr(x) ((x)*(x))



6.f Benign Errors

Benign errors cause a syntax error when they occur, alerting the programmer that there is an error.


The most common benign error is placing a semicolon on the end of a macro definition. Macro definitions are not C declarations and do not require a terminating semicolon. The preprocessor will silently include the semicolon as part of the replacement text, and output it in the macro expansion where the macro is called. The extra semicolon is often harmless, but in some situations it will cause a syntax error.


A similar error made by novice programmers is the incorrect definition of symbolic constants using assignment notation. The = sign in the symbolic constant definition below is incorrect:

#define MAX = 30

As with the extra semicolon, the = sign becomes part of the replacement text and any use of the symbolic constant will usually cause a syntax error.


 

7. Cluttered compile time environment

The compile-time environment of a typical compilation is cluttered with hundreds (or thousands!) of things that you typically have little or no awareness of.  These things sometimes have dangerously common names, leading to accidents that can be virtually impossible to spot.


#include <stdio.h>

#define BUFFSIZE 2048 

long foo[BUFSIZ];        //note spelling of BUFSIZ != BUFFSIZE


This compiles without error, but will fail in predictably awful and mysterious ways, because BUFSIZ is a symbol defined by stdio.h.  A typo/braino like this can be virtually impossible to find if the distance between the the #define and the error is greater than in this trivial example. 

 

Part three in the next fifteen days.

Gg1