Calling function with side effects inside expression
Asked Answered
F

2

8

I thought I understand how sequence points work in C++, but this GeeksQuiz question puzzled me:

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;
}

The “correct” answer to this question says it prints 6561. Indeed, in VS2013 it does. But isn't it UB anyway because there is no guarantee which will be evaluated first: f(x, c) or x. We get 6561 if f(x, c) is evaluated first: the whole thing turns into five recursive calls: the first four (c = 5, 4, 3, 2) continue on, the last one (c = 1) terminates and returns 1, which amounts to 9 ** 4 in the end.

However, if x was evaluated first, then we'd get 6 * 7 * 8 * 9 * 1 instead. The funny thing is, in VS2013 even replacing f(x, c) * x with x * f(x, c) doesn't change the result. Not that it means anything.

According to the standard, is this UB or not? If not, why?

Fishnet answered 7/10, 2016 at 9:50 Comment(1)
clang gives 3024 for x * f(x, c) (and 6561 for f(x, c) * x).Unfix
C
4

This is UB.

n4140 §1.9 [intro.execution]/15

Except where noted, evaluations of operands of individual operators and of subexpressions of individual expressions are unsequenced. [...] If a side effect on a scalar object is unsequenced relative to [...] value computation using the value of the same scalar object [...] the behavior is undefined.

Multiplicative operators don't have sequencing explicitly noted.

Career answered 7/10, 2016 at 10:9 Comment(0)
F
2

This is UB

Order of evaluation of the operands of almost all C++ operators (including the order of evaluation of function arguments in a function-call expression and the order of evaluation of the subexpressions within any expression) is unspecified. The compiler can evaluate operands in any order, and may choose another order when the same expression is evaluated again.

There are exceptions to this rule which are noted below.

Except where noted below, there is no concept of left-to-right or right-to-left evaluation in C++. This is not to be confused with left-to-right and right-to-left associativity of operators: the expression f1() + f2() + f3() is parsed as (f1() + f2()) + f3() due to left-to-right associativity of operator+, but the function call to f3 may be evaluated first, last, or between f1() or f2() at run time.

Foretop answered 7/10, 2016 at 10:22 Comment(0)

© 2022 - 2025 — McMap. All rights reserved.