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

我在文本C如何编程中遇到了一个声明:

“不应将具有副作用的表达式(即,修改变量值)传递给宏,因为宏参数可能会被多次评估。”

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

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

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

现在让我们像这样“调用”宏:

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

现在,如果MAX是正常函数,我们可以预期xy会递增一次,对吗? 但是因为它是一个宏,所以“call”被替换为:

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

如您所见,变量y将递增两次 ,一次在条件中,一次作为三元运算符的最终结果。

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


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

 #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对象一次或两次(取决于宏参数,这里一次)在宏定义中)。

副作用可以定义为:

表达式的评估产生一些东西,如果另外执行环境的状态发生变化,则表示表达式(其评估)具有一些副作用。 例如:

  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