CertC-PRE12ΒΆ
Do not define unsafe macros
Required inputs: IR
An unsafe function-like macro is one that, when expanded, evaluates its argument more than once or does not evaluate it at all. Contrasted with function calls, which always evaluate each of their arguments exactly once, unsafe function-like macros often have unexpected and surprising effects and lead to subtle, hard-to-find defects (see PRE31-C. Avoid side effects in arguments to unsafe macros). Consequently, every function-like macro should evaluate each of its arguments exactly once. Alternatively and preferably, defining function-like macros should be avoided in favor of inline functions (see PRE00-C. Prefer inline or static functions to function-like macros).
Noncompliant Code Example (Multiple Argument Evaluation)
The most severe problem with unsafe function-like macros is side effects of macro arguments, as shown in this noncompliant code example:
#define ABS(x) (((x) < 0) ? -(x) : (x))
void f(int n) {
int m;
m = ABS(++n);
/* ... */
}
The invocation of the
ABS() macro in this noncompliant code example expands to the
following code. The resulting code has well-defined behavior but causes
n to be incremented twice rather than once, which may be
surprising to those unfamiliar with the
implementation
of the macro or unaware that they are using a macro in the first place.
m = (((++n) < 0) ? -(++n) : (++n));
Compliant Solution (Inline Function)
A possible and preferable compliant solution is to define an inline function with equivalent but unsurprising semantics:
inline int Abs(int x) {
return x < 0 ? -x : x;
}
Compliant Solution (Language Extension)
Some implementations provide language extensions that make it possible to
define safe function-like macros, such as the macro
ABS(), 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 because relying on
implementation-defined extensions introduces undesirable platform dependencies
that may make the resulting code nonportable, such solutions should be avoided
in favor of portable ones wherever possible (see
MSC14-C.
Do not introduce unnecessary platform dependencies).
Another GCC extension known as statement expression makes it possible for the block statement to appear where an expression is expected. The statement expression extension establishes a scope (note the curly braces) and any declarations in it are distinct from those in enclosing scopes.
#define ABS(x) __extension__ ({ __typeof (x) __tmp = x; __tmp < 0 ? - __tmp : __tmp; })
Risk Assessment
Defining an unsafe macro leads to invocations of the macro with an argument that has side effects, causing those side effects to occur more than once. Unexpected or undefined program behavior can result.
| Rule | Severity | Likelihood | Remediation Cost | Priority | Level |
|---|---|---|---|---|---|
| PRE12-C | Low | Probable | Low | P6 | L2 |
Related Guidelines
| SEI CERT C++ Coding Standard | VOID PRE10-CPP. Do not define unsafe macros |
Possible Messages
Key |
Text |
Severity |
Disabled |
|---|---|---|---|
parameter_multiply_evaluated |
Macro parameter is evaluated more than once |
None |
False |
parameter_not_evaluated |
Macro parameter is not evaluated |
None |
False |
Options
This rule shares the following common options: exclude_in_macros, exclude_messages_in_system_headers, excludes, extend_exclude_to_macro_invocations, includes, justification_checker, languages, post_processing, provider, report_at, severity
The following places define options that affect this rule: Stylechecks, Analysis-GlobalOptions
This rule has no individual options.