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

时间:2015-08-29 08:22:51

标签: c macros side-effects

我在文章C How to Program:

中看到了一个声明

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

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

3 个答案:

答案 0 :(得分:10)

经典示例是一个用于计算两个值的最大值的宏:

#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将增加两次,一次在条件中,一次作为ternary operator的最终结果。

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

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

#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);

这可能是正确的。

答案 1 :(得分:1)

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

这是一个副作用的例子:

i++;

这是在宏中使用副作用:

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

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

c = MAX(a, b++);

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

答案 2 :(得分:0)

副作用可以定义为:

  

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

 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