Macros are dangerous because their use resembles that of real functions, but they have different semantics. The 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.)
...
In this noncompliant code example, the macro CUBE()
has undefined behavior when passed an expression that contains side effects.:
Code Block | ||||
---|---|---|---|---|
| ||||
#define CUBE(X) ((X) * (X) * (X)) /* ... */ int i = 2; int a = 81 / CUBE(++i); |
...
When the macro definition is replaced by an inline function, the side effect is executed only once before the function is called.:
Code Block | ||||
---|---|---|---|---|
| ||||
inline int cube(int i) { return i * i * i; } /* ... */ int i = 2; int a = 81 / cube(++i); |
...
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 | ||||
---|---|---|---|---|
| ||||
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 | ||||
---|---|---|---|---|
| ||||
#define F(x) (++operations, ++calls_to_F, 2 * x) #define G(x) (++operations, ++calls_to_G, x + 1) /* ... */ y = F(x) + G(x); |
...
The execution of functions, including inline functions, cannot be interleaved, so problematic orderings are not possible.:
Code Block | ||||
---|---|---|---|---|
| ||||
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); |
...