【问题标题】:What is argument evaluation?什么是论证评估?
【发布时间】:2016-03-14 16:33:04
【问题描述】:

赫伯特·席尔特说:

在某些情况下,应该使用真正的函数来代替类函数宏,例如:要最小化代码大小或不能多次评估参数。

他所说的是什么意思“一个参数不能被多次评估?”

【问题讨论】:

标签: c


【解决方案1】:

让我们用一个宏来计算两个值的最大值:

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

然后我们这样使用它:

int x = 5;
int y = 10;
int max = MAX(x++, y++);

然后宏展开为

int max = ((x++) < (y++) ? (x++) : (y++));

如您所见,x 或 y 上的递增操作将发生两次,而不是如果您有一个函数,其中您传递的每个参数只计算一次,则会发生这种情况。


另一个重要的一点是在宏中使用括号。让我们再看一个简单的宏:

#define MUL(a, b) a * b

现在如果你调用宏

int sum = MUL(x + 3, y - 2);

那么展开就变成了

int sum = x + 3 * y - 2;

由于operator precedence等于

int sum = x + (3 * y) - 2;

如果有人期望(x + 3) * (y - 2),通常与预期的不太一样。

这个问题也可以通过函数来​​“解决”。

【讨论】:

  • @hacks 这是不正确的。正如 Joachim 在他的回答中所说,MAX(x++, y++) 扩展为((x++) &lt; (y++) ? (x++) : (y++));,因此被评估了两次。对应函数max(x++, y++),每个参数只求值一次
  • @hacks x++ 和 y++ 表达式都作为条件的一部分进行计算。然后根据三元表达式中的条件再次评估x++ 或y++ 之一。
  • @hacks 但是宏只是简单地扩展到宏的主体,没有评估“参数”,因为它们只是粘贴到扩展的宏中。当使用我的答案中定义的 MAX 之类的宏时,它是处理宏扩展的预处理器,编译器本身看不到宏调用,它只看到扩展的三元表达式。
  • #define MUL(a, b) ((a) * (b)) 缓解了第二个问题
  • 我认为您的第二个示例对这个问题没有帮助。正如 Erbureth 所说,你可以通过添加一些括号来解决这个问题——所以这是一个使用格式不正确的宏的例子,而不是在你应该使用函数的地方使用宏的例子。
【解决方案2】:

有时参数有副作用。

例如,i++ 的值为i,但i 增加了1。因此,下一个i++ 的值将是i + 1。

在宏中,每次调用参数时都会对参数进行求值,从而产生结果值;在函数中,(实际)参数被评估并复制到函数内部的(正式)参数,消除副作用。

当实现一个函数时,你并不关心副作用。但是,隐式类型提升和强制转换可能反而容易出错。

【讨论】:

    猜你喜欢
    • 2016-12-18
    • 2015-02-09
    • 2011-03-29
    • 2015-09-12
    • 2016-11-24
    • 2018-05-04
    • 2017-09-27
    • 1970-01-01
    • 2011-11-18
    相关资源
    最近更新 更多