Macros are error prone because they rely on text substitution and do not perform type checking. For example, this macro:
works great when using an integer:
square(5)
but does very strange things when used with expressions:
square(1 + 2) --> 1 + 2 * 1 + 2 --> 1 + 2 + 2 --> 5 square(x++) --> x++ * x++ --> increments x twice
Putting parentheses in arguments helps, but does not completely eliminate these problems.
When macros contain multiple statements, you may have problems with the control-flow constructs:
#define swap(x, y) t = x; x = y; y = t; if (x < y) swap(x, y); --> if (x < y) t = x; x = y; y = t; --> if (x < y) { t = x; } x = y; y = t;
The usual strategy to fix this is to put statements in the "do {...} while (0)" loop.
If you have two structures that contain a field with the same name but different semantics, the same macro can work with both, which will lead to strange results:
struct shirt { int numButtons; }; struct webpage { int numButtons; }; #define num_button_holes(shirt) ((shirt).numButtons * 4) struct webpage page; page.numButtons = 2; num_button_holes(page) -> 8
Finally, macros can be difficult to debug, leading to strange syntax or runtime errors that you need to open up for understanding (for example, using gcc -E), since debuggers cannot go through macros, as in this example:
#define print(x, y) printf(xy) /* accidentally forgot comma */ print("foo %s", "bar") /* prints "foo %sbar" */
Built-in functions and constants help to avoid many of these macro problems, but are not always applicable. In cases where macros are deliberately used to determine polymorphic behavior, inadvertent polymorphism can be difficult to avoid. C ++ has a number of functions, such as templates, that help create complex polymorphic constructs in a type-safe way without using macros; see Stroustrup C ++ Programming Language for details.