什么是有副作用的表达式,为什么不应该将它们传递给宏?

What are expressions with side effects and why should they be not passed to a macro?

我在 C How to Program 课文中看到了一个语句:

"Expressions with side effects (i.e., variable values are modified) should not be passed to a macro because macro arguments may be evaluated more than once.".

我的问题是什么是有副作用的表达式,为什么不将它们传递给宏?

经典例子是计算两个值最大值的宏:

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

现在让 "call" 宏像这样:

int x = 5;
int y = 7;
int z = MAX(x++, y++);

现在如果 MAX 是一个普通函数,我们会期望 xy 会递增一次,对吗?但是,因为它是一个宏,所以 "call" 被替换为这样:

int z = ((x++) > (y++) ? (x++) : (y++));

如您所见,变量 y 将递增 两次 ,一次在条件中,一次作为 ternary operator 的最终结果。

这是具有副作用的表达式(post-增量表达式)和宏扩展的结果。


在相关说明中,宏还存在其他危险。例如让我们采用这个简单的宏:

#define MUL_BY_TWO(x)  (x * 2)

看起来很简单吧?但是现在如果我们这样使用它会怎样:

int result = MUL_BY_TWO(a + b);

那会扩展成

int result = (a + b * 2);

正如您所希望知道的,乘法的优先级高于加法,因此表达式 a + b * 2 等同于 a + (b * 2),这可能不是宏编写者的意图。这就是为什么宏的参数应该放在它们自己的括号内:

#define MUL_BY_TWO(x)  ((x) * 2)

那么扩展会是

int result = ((a + b) * 2);

这可能是正确的。

简单来说,副作用就是写入对象或读取易失性对象。

所以一个副作用的例子:

i++;

这是在宏中使用副作用:

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

int a = 42;
int b = 1;
int c;

c = MAX(a, b++);

危险与参数按值传递的函数相反,您可能会在宏中修改 b 对象一两次(取决于宏参数,这里是一次),因为宏的工作方式(通过替换宏定义中的 b++ 标记)。

副作用可以定义为:

Evaluation of an expression produces something and if in addition there is a change in the state of the execution environment it is said that the expression (its evaluation) has some side effect(s). For example:

 int x = y++; //where y is also an int

除了初始化操作之外,由于 ++ 运算符的副作用,y 的值也会发生变化。

现在考虑一个整数平方的宏:

 #define Sq(a) a*a
 main()
 {
    int a=6;
    int res=Sq(a);
    printf("%d\n",res);
    res=Sq(++a);
    printf("%d",res);
 }

您希望输出为

36 49

但是输出是

36 64

因为宏会导致文本替换并且

res 变为 (++a)*(++a) 即 8*8=64

因此我们不应该将有副作用的参数传递给宏。 (http://ideone.com/mMPQLP)