宏替换

最简单的宏替换形式是定义一个 manifest constant,如

#define ARRSIZE 100
int array[ARRSIZE];

这定义了一个类似函数的宏,它将变量乘以 10 并存储新值:

#define TIMES10(A) ((A) *= 10)

double b = 34;
int c = 23;

TIMES10(b);   // good: ((b) *= 10);
TIMES10(c);   // good: ((c) *= 10);
TIMES10(5);   // bad:  ((5) *= 10);

替换是在对程序文本的任何其他解释之前完成的。在第一次调用 TIMES10 时,定义中的 A 名称被替换为 b,然后将这样扩展的文本放在呼叫的位置。请注意,TIMES10 的这个定义并不等同于

#define TIMES10(A) ((A) = (A) * 10)

因为这可以评估替换 A 两次,这可能会产生不必要的副作用。

下面定义了一个类似函数的宏,其值是其参数的最大值。它具有为任何兼容类型的参数工作以及生成内联代码而不需要函数调用的开销的优点。它的缺点是第二次评估其中一个或另一个参数(包括副作用),并且如果多次调用则生成比函数更多的代码。

#define max(a, b) ((a) > (b) ? (a) : (b))

int maxVal = max(11, 43);              /* 43 */
int maxValExpr = max(11 + 36, 51 - 7); /* 47 */

/* Should not be done, due to expression being evaluated twice */
int j = 0, i = 0;
int sideEffect = max(++i, ++j);       /* i == 4 */

因此,在生产代码中通常会避免多次评估其参数的宏。从 C11 开始,_Generic 功能允许避免这种多次调用。

宏扩展中的丰富括号(定义的右侧)确保参数和结果表达式被正确绑定并且很好地适合调用宏的上下文。