Add section on the misuse of macros.

This commit is contained in:
Wade Mealing 2020-06-08 18:15:11 +10:00
parent 4d7432da07
commit e3718be004

View file

@ -51,3 +51,85 @@ careful analysis and comparison with the compiler documentation
is required to check if propagating the attribute is
appropriate. Incorrectly applied attributes can result in
undesired behavioral changes in the compiled code.
[[sect-Defensive_Coding-C-Common-Mistakes]]
=== Common mistakes
==== Mistakes in macros
A macro is a name given to a block of C statements as a pre-processor
directive. Being a pre-processor the block of code is transformed by
the compiler before being compiled.
A macro starts with the preprocessor directive, #define. It can
define a single value or any 'substitution', syntactically valid or
not.
A common mistake when working with macros is that programmers treat
arguments to macros like they would functions. This becomes an issue
when the argument may be expanded multiple times in a macro.
For example:
macro-misuse.c
[source,C]
----
#define simple(thing) do { \
if (thing < 1) { \
y = thing; \
} \
else if (thing > 100) { \
y = thing * 2 + thing; \
} \
else { \
y = 200; \
} \
} while (0)
int main(void) {
int x = 200;
int y = 0;
simple(x++);
return 0;
}
----
Each pass through the simple() macro would mean that x could be
expanded in-place each time 'thing' was mentioned.
The 'main' function would be processed and expanded as follows:
macro-misuse-post-processing.c
[source,C]
----
int main(void) {
int x = 200;
int y = 0;
do {
if ( x++ < 1) {
y = x++;
}
else if (thing > 100) {
y = x++ * 2 + x++;
}
else {
x = 200;
}
} while (0)
return 0;
}
----
Each evaluation of the argument to 'simple' (x++) would be executed
each time it was referenced.
While this may be 'expected' behaviour by the original creator, large
projects may have programmers who were unaware of how the macro may
expand and this may introduce unexpected behaviour, especially if the
value is later used as indexing into an array or able to be
overflowed.