Add section on the misuse of macros.
This commit is contained in:
parent
4d7432da07
commit
e3718be004
1 changed files with 82 additions and 0 deletions
|
@ -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.
|
||||
|
||||
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue