...
The most severe problem with unsafe function-like macros is side effects of macro arguments, as shown in this noncompliant code example.
Code Block | ||||
---|---|---|---|---|
| ||||
#define ABS(x) (((x) < 0) ? -(x) : (x)) void f(int n) { int m; m = ABS(++n); /* undefined behavior */ /* ... */ } |
The invocation of the ABS()
macro in this noncompliant code example expands to the code below. Since the resulting expression modifies an object more than once, its behavior is undefined. (See rule EXP30-C. Do not depend on order of evaluation between sequence points.)
Code Block | ||||
---|---|---|---|---|
| ||||
m = (((++n) < 0) ? -(++n) : (++n)); /* undefined behavior */ |
...
A possible and preferable compliant solution is to define an inline function with equivalent but safe semantics:
Code Block | ||||
---|---|---|---|---|
| ||||
inline int Abs(x) { return x < 0 ? -x : x; } |
...
Some implementations provide language extensions that make it possible to define safe function-like macros such as the macro ABS()
above that would otherwise require evaluating their arguments more than once. For example, the gcc extension Statements and Declarations in Expressions makes it possible to implement the macro ABS()
in a safe way. Note, however, that since relying on implementation-defined extensions introduces undesirable platform dependencies that may make the resulting code non-portable, such solutions should be avoided in favor of portable ones wherever possible. (See recommendation MSC14-C. Do not introduce unnecessary platform dependencies.)
Code Block | ||||
---|---|---|---|---|
| ||||
#define ABS(x) ({int tmp = (x); tmp < 0 ? -tmp : tmp; }) |
...