Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.
Wiki Markup
Macros are dangerous because their use resembles that of real functions, but they have different semantics. C99 adds inline functions to the C programming language.  Inline functions should be preferred over macros when they can be used interchangeably.  Making a function an inline function suggests that calls to the function be as fast as possible by using, for example, an alternative to the usual function call mechanism, such as _inline substitution_. (See also [PRE31-C. Never invoke an unsafe macro with arguments containing assignment, increment, decrement, volatile access, or function call], [PRE01-C. Use parentheses within macros around parameter names], and [PRE02-C. Macro replacement lists should be parenthesized].)

...



Inline substitution is not textual substitution, nor does it create a new function. For example, the expansion of a macro used within the body of the function uses the definition it had at the point the function body appeared, and not where the function is called; and identifiers refer to the declarations in scope where the body occurs.

...



{mc}

hasfddsf

{mc}


Arguably, a decision to inline a function is a low-level optimization detail that the compiler should make without programmer input. The use of inline functions should be evaluated on the basis of (a) how well they are supported by targeted compilers, (b) what (if any) impact they have on the performance characteristics of your system, and (c) portability concerns.  Static functions are often as good as inline functions and are supported in C90 (unlike inline functions).

h2.

...

 Noncompliant Code Example

...



In this noncompliant code example, the macro {{CUBE()}} has [undefined behavior|BB. Definitions#undefined behavior] when passed an expression that contains side effects.

...


{code
:bgColor
=#FFCCCC
}
#define CUBE(X) ((X) * (X) * (X))
/* ... */
int i = 2;
int a = 81 / CUBE(++i);
{code}
For this example, the initialization for {{a}} expands to

...


{code
:bgColor
=#FFCCCC
}
int a = 81 / ((++i) * (++i) * (++i));
{code}
which is undefined (see [EXP30-C. Do not depend on order of evaluation between sequence points]).

...



h2. Compliant Solution

...



When the macro definition is replaced by an inline function, the side effect is executed only once before the function is called.

...


{code
:bgColor
=#ccccff
}
inline int cube(int i) {
  return i * i * i;
}
/* ... */
int i = 2;
int a = 81 / cube(++i);

Noncompliant Code Example

...


{code}

h2. Noncompliant Code Example

In this noncompliant code example, the programmer has written a macro called {{EXEC_BUMP()}} to call a specified function and increment a global counter \[[Dewhurst 02|AA. C References#Dewhurst 02]\].  When the expansion of a macro is used within the body of a function, as in this example, identifiers refer to the declarations in scope where the body occurs.  As a result, when the macro is called in the {{aFunc()}} function, it inadvertently increments a local counter with the same name as the global variable. Note that this example also violates [DCL01-C. Do not reuse variable names in subscopes].

...


{code
:bgColor
=#FFCCCC
}
size_t count = 0;

#define EXEC_BUMP(func) (func(), ++count)

void g(void) {
  printf("Called g, count = %zu.\n", count);
}

void aFunc(void) {
  size_t count = 0;
  while (count++ < 10) {
    EXEC_BUMP(g);
  }
}
{code}
The result is that invoking {{aFunc()}} (incorrectly) prints out the following line five times:

...


{code
}
Called g, count = 0.

Compliant Solution

...


{code}

h2. Compliant Solution

In this compliant solution, the {{EXEC_BUMP()}} macro is replaced by the inline function {{exec_bump()}}.  Invoking {{aFunc()}} now (correctly) prints the value of {{count}} ranging from 0 to 9.

...


{code
:bgColor
=#ccccff
}
size_t count = 0;

void g(void) {
  printf("Called g, count = %zu.\n", count);
}

typedef void (*exec_func)(void);
inline void exec_bump(exec_func f) {
  f();
  ++count;
}

void aFunc(void) {
  size_t count = 0;
  while (count++ < 10) {
    exec_bump(g);
  }
}
{code}
The use of the inline function binds the identifier {{count}} to the global variable when the function body is compiled.  The name cannot be re-bound to a different variable (with the same name) when the function is called.

...



h2. Noncompliant Code Example

...



Unlike functions, the execution of macros can interleave.  Consequently, two macros that are harmless in isolation can cause undefined behavior when combined in the same expression.

...



In this example, {{F()}} and {{G()}} both increment the global variable {{operations}}, which causes problems when the two macros are used together.

...


{code
:bgColor
=#FFCCCC
}
#define F(x) (++operations, ++calls_to_F, 2*x)
#define G(x) (++operations, ++calls_to_G, x + 1)

/* ... */

y = F(x) + G(x);
{code}
The variable {{operations}} is both read and modified twice in the same expression, so it can receive the wrong value if, for example, the following ordering occurs:

...


{noformat
}
read operations into register 0
read operations into register 1
increment register 0
increment register 1
store register 0 into operations
store register 1 into operations
{noformat}
This noncompliant code example also violates [EXP30-C. Do not depend on order of evaluation between sequence points].

...



h2. Compliant Solution

...



The execution of functions, including inline functions, cannot be interleaved, so problematic orderings are not possible.

...


{code
:bgColor
=#ccccff
}
inline int f(int x) {
   ++operations;
   ++calls_to_f;
   return 2*x;
}

inline int g(int x) {
   ++operations;
   ++calls_to_g;
   return x + 1;
}

/* ... */

y = f(x) + g(x);

{code}

h3. Platform-Specific

...

 Details

GNU C (and some other compilers) supported inline functions before they were added to C99 and as a result have significantly different semantics.  Richard Kettlewell provides a good explanation of differences between the C99 and GNU C rules \[[Kettlewell 03|AA. C References#Kettlewell 03]\].

...

Exceptions



h2. Exceptions

*PRE00-EX1:* Macros can be used to implement _local functions_ (repetitive blocks of code that have access to automatic variables from the enclosing scope) that cannot be achieved with inline functions.

...



*PRE00-EX2:* Macros can also be made to support certain forms of _lazy calculation_ that cannot be achieved with an inline function. For example,

...


{code
}
#define SELECT(s, v1, v2) ((s) ? (v1) : (v2))
{code}
calculates only one of the two expressions depending on the selector's value.

...



*PRE00-EX3:* Macros can be used to yield a compile-time constant.  This is not always possible using inline functions, as shown by the following example:

...


{code
}
#define ADD_M(a, b) ((a) + (b))
static inline add_f(int a, int b) {
  return a + b;
}
{code}
In this example, the {{ADD_M(3,4)}} macro invocation yields a constant expression, while the {{add_f(3,4)}} function invocation does not.

...



*PRE00-EX4:* Macros can be used to implement type-generic functions that cannot be implemented in the C language without the aid of a mechanism such as C+\+ templates.

...



An example of the use of function-like macros to create type-generic functions is shown in [MEM02-C. Immediately cast the result of a memory allocation function call into a pointer to the allocated type].

...



Type-generic macros may also be used, for example, to swap two variables of any type, provided they are of the same type.

...



*PRE00-EX5:* Macro parameters exhibit call-by-name semantics, whereas functions are call by value.  Macros must be used in cases where call-by-name semantics are required.

...



h2. Risk Assessment

...



Improper use of macros may result in undefined behavior.

...

Recommendation

Severity

Likelihood

Remediation Cost

Priority

Level

PRE00-C

medium

unlikely

medium

P4

L3

Automated Detection

...


|| Recommendation || Severity || Likelihood || Remediation Cost || Priority || Level ||
| PRE00-C | medium | unlikely | medium | {color:green}{*}P4{*}{color} | {color:green}{*}L3{*}{color} |

h3. Automated Detection

The LDRA tool suite V 7.6.0 can detect violations of this recommendation.

h3.

...

 Related Vulnerabilities

...



Search for vulnerabilities resulting from the violation of this rule on the [CERT website

...

Other Languages

...

|https://www.kb.cert.org/vulnotes/bymetric?searchview&amp;query=FIELD+KEYWORDS+contains+PRE00-C].

h3. Other Languages

This rule appears in the C++ Secure Coding Standard as [cplusplus:PRE00-CPP. Prefer inline or static functions to function-like macros].

h2.

...

References

...

 References

\[[FSF 05|AA. C References#FSF 05]\] Section 5.34, "[An Inline Function Is as Fast as a Macro|http://gcc.gnu.org/onlinedocs/gcc/Inline.html]"
\[[Dewhurst 02|AA. C References#Dewhurst 02]\] Gotcha #26, "#define Pseudofunctions"
\[[ISO/IEC 9899:1999|AA. C References#ISO/IEC 9899-1999]\] Section 6.7.4, "Function specifiers"
\[[ISO/IEC PDTR 24772|AA. C References#ISO/IEC PDTR 24772]\] "NMP Pre-processor Directives"
\[[Kettlewell 03|AA. C References#Kettlewell 03]\]
\[[MISRA 04|AA. C References#MISRA 04]\] Rule 19.7
\[[Summit 05|AA. C References#Summit 05]\] Question 10.4

...



----
[!CERT C Secure Coding Standard^button_arrow_left.png!|01. Preprocessor (PRE)

...

]&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;[!CERT C Secure Coding Standard^button_arrow_up.png!|01. Preprocessor (PRE)]&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;[!CERT C Secure Coding Standard^button_arrow_right.png!|PRE01-C. Use parentheses within macros around parameter names]