在表达式内调用具有副作用的函数

时间:2016-10-07 09:50:37

标签: c++ sequence-points

我以为我理解序列点在C ++中是如何工作的,但是this GeeksQuiz question让我困惑:

int f(int &x, int c) {
    c = c - 1;
    if (c == 0) return 1;
    x = x + 1;
    return f(x, c) * x;
}

int main() {
    int p = 5;
    cout << f(p, p) << endl;
    return 0;
}

这个问题的“正确”答案是它打印6561.事实上,在VS2013中确实如此。但是不管它是UB,因为无法保证首先评估:f(x, c)x。如果首先评估f(x, c),我们得到6561:整个事情变成五个递归调用:前四个(c = 5, 4, 3, 2)继续,最后一个(c = 1)终止并返回1,最后相当于9 ** 4

但是,如果首先评估x,那么我们会得到6 * 7 * 8 * 9 * 1。有趣的是,在VS2013中,即使用f(x, c) * x替换x * f(x, c)也不会改变结果。并不意味着什么。

根据标准,这个UB是不是?如果没有,为什么?

2 个答案:

答案 0 :(得分:4)

这是UB。

  

n4140§1.9[intro.execution] / 15

     

除非另有说明,否则评估   个体经营者和个体子表达的操作数   表达式没有排序。 [...]如果对标量物体产生副作用   使用值的值相对于值计算是无序的   相同的标量对象[...]行为未定义。

乘法运算符没有明确指出序列。

答案 1 :(得分:2)

这是UB

  

几乎所有C ++运算符的操作数的评估顺序(包括函数调用表达式中函数参数的评估顺序以及任何表达式中子表达式的评估顺序)都未指定。编译器可以按任何顺序计算操作数,并且可以在再次计算同一表达式时选择另一个顺序。

     

此规则有例外情况,如下所述。

     

除非下文所述,否则没有从左到右或从左到右的概念   C ++中从右到左的评估。这是不要混淆的   运营商的从左到右和从右到左的相关性:   表达式f1()+ f2()+ f3()被解析为(f1()+ f2())+ f3()由于   operator +的从左到右的关联性,但函数调用f3   可以在运行时第一次,最后一次或在f1()或f2()之间进行评估。