Versions Compared

Key

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

...

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))
/* ... */
int i = 2;
int a = 81 / CUBE(++i);

For this example, the initialization for a expands to

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

...

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

...