...
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);
|
For this example, the initialization for a
expands to
Code Block |
---|
|
int a = 81 / ((++i) * (++i) * (++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);
|
...
Wiki Markup |
---|
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 2002|AA. Bibliography#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 recommendation [DCL01-C. Do not reuse variable names in subscopes]. |
Code Block |
---|
|
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);
}
}
|
...
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);
}
}
|
...
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);
|
...