Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.

Macros are dangerous because their use resembles that of real functions, but they have different semantics. The C standard adds inline functions inline function-specifier was introduced to the C programming language in the C99 standard. 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. Avoid side - effects in arguments to unsafe macros, PRE01-C. Use parentheses within macros around parameter names, and PRE02-C. Macro replacement lists should be parenthesized.)

...

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 C (unlike inline functions).

Noncompliant Code Example

In this noncompliant code example, the macro CUBE() has undefined behavior when passed an expression that contains side effects.:

Code Block
bgColor#FFCCCC
langc
#define CUBE(X) ((X) * (X) * (X))
/* ... */
 
void func(void) {
  int i = 2;
  int a = 81 / CUBE(++i);
  /* ... */
}

For this example, the initialization for a expands to

#FFCCCC
Code Block
bgColor
langc
int a = 81 / ((++i) * (++i) * (++i));

which is undefined . (See see EXP30-C. Do not depend on the order of evaluation between sequence points.)for side effects).

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 Block
bgColor#ccccff
langc
inline int cube(int i) {
  return i * i * i;
}
 
/* ... */
void func(void) {
  int i = 2;
  int a = 81 / cube(++i);
  /* ... */ 
}

Noncompliant Code Example

...

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 Block
bgColor#ccccff
langc
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);
  }
}

...

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 Block
bgColor#FFCCCC
langc
int operations = 0, calls_to_F = 0, calls_to_G = 0;
 
#define F(x) (++operations, ++calls_to_F, 2 * x)
#define G(x) (++operations, ++calls_to_G, x + 1)

/* ... */

void func(int x) {
  int y = F(x) + G(x);

}

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:

...

This noncompliant code example also violates EXP30-C. Do not depend on the order of evaluation between sequence pointsfor side effects.

Compliant Solution

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

Code Block
bgColor#ccccff
langc
int operations = 0, calls_to_F = 0, calls_to_G = 0;
 
inline int f(int x) {
   ++operations;
   ++calls_to_fF;
   return 2 * x;
}

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

/* ... */

 
void func(int x) {
  int y = f(x) + g(x);

}

Platform-Specific Details

GNU C (and some other compilers) supported inline functions before they were added to the C standard andStandard and, as a result, have significantly different semantics. Richard Kettlewell provides a good explanation of differences between the C99 and GNU C rules [Kettlewell 2003].

Exceptions

PRE00-C-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-C-EX2: Macros can be used for concatenating tokens or performing stringification. For example,

...

calculates only one of the two expressions depending on the selector's value. See PRE05-C. Understand macro replacement when concatenating tokens or performing stringification for more information.

PRE00-C-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:

...

In this example, the ADD_M(3,4) macro invocation yields a constant expression, while but the add_f(3,4) function invocation does not.

PRE00-C-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-C-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.

...

Improper use of macros may result in undefined behavior.

Recommendation

Severity

Likelihood

Remediation Cost

Priority

Level

PRE00-C

medium

Medium

unlikely

Unlikely

medium

Medium

P4

L3

Automated Detection

ToolVersionCheckerDescription
Astrée
Include Page
Astrée_V
Astrée_V

macro-function-like
function-like-macro-expansion

Fully checked
Axivion Bauhaus Suite

Include Page
Axivion Bauhaus Suite_V
Axivion Bauhaus Suite_V

CertC-PRE00
CodeSonar
Include Page
CodeSonar_V
CodeSonar_V

LANG.PREPROC.FUNCMACRO

Function-Like Macro

ECLAIR

Include Page
ECLAIR_V
ECLAIR_V

CC2.PRE00

Fully implemented

Helix QAC

Include Page
Helix QAC_V
Helix QAC_V

C3453
Klocwork
Include Page
Klocwork_V
Klocwork_V
MISRA.DEFINE.FUNC
LDRA tool suite
Include Page
LDRA_V
LDRA_V

340 S

Fully implemented

PRQA QA-C Include PagePRQA_VPRQA_V3453Fully implemented

...

Enhanced enforcement

Parasoft C/C++test
Include Page
Parasoft_V
Parasoft_V
CERT_C-PRE00-a

A function should be used in preference to a function-like macro

PC-lint Plus

Include Page
PC-lint Plus_V
PC-lint Plus_V

9026

Assistance provided

Polyspace Bug Finder

Include Page
Polyspace Bug Finder_V
Polyspace Bug Finder_V

CERT C: Rec. PRE00-CChecks for use of function-like macro instead of function (rec. fully covered)


RuleChecker
Include Page
RuleChecker_V
RuleChecker_V
macro-function-like
function-like-macro-expansion
Fully checked
SonarQube C/C++ Plugin
Include Page
SonarQube C/C++ Plugin_V
SonarQube C/C++ Plugin_V
S960

Related Vulnerabilities

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

Related Guidelines

SEI CERT C++
Secure
Coding StandardVOID PRE00-CPP. Avoid defining macros
ISO/IEC TR 24772:2013
"NMP
Pre-processor
directives"
Directives [NMP]
MISRA
Rule 19.7
C:2012Directive 4.9 (advisory)

Bibliography

[Dewhurst 2002]Gotcha #26, "#define Pseudofunctions"
[FSF 2005]Section 5.34, "An Inline Function Is as Fast as a Macro
"[ISO/IEC 9899:2011]Section 6.7.4,
"
Function Specifiers"
[Kettlewell 2003]
 

[Summit 2005]
AA. Bibliography#Summit 05
Question 10.4

...


...

Image Modified Image Modified Image Modified