什么是有副作用的表达式,为什么不应该将它们传递给宏?
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
是一个普通函数,我们会期望 x
和 y
会递增一次,对吗?但是,因为它是一个宏,所以 "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)
我在 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
是一个普通函数,我们会期望 x
和 y
会递增一次,对吗?但是,因为它是一个宏,所以 "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)