4

Will this code always result in the same result?

return c * (t /= d) * t * t + b;

So I expect:

return ((c * (t / d) ^ 3) + b);

But I am not sure if the compiler can also interpret it as:

return ((c * t * t * (t / d)) + b)

I have searched in the C standard but could not find an answer, I know that x = x++ is undefined but here I am not sure because of the () around the t /= d which I think force the compiler to first calculate that statement.

sepp2k
  • 363,768
  • 54
  • 674
  • 675
laserbeamer
  • 377
  • 1
  • 4
  • 10
  • 1
    No, they don't force compiler. Pretty much only `;` does, to certain degree. – Joker_vD Jun 26 '16 at 22:57
  • 2
    Parenthesis in expression override operator priority not order of evaluation. It could be that one compiler implementation may change evaluation order to ensure proper operator priority, but that is not necessary. I would not count on the order of evaluation. – P. Kouvarakis Jun 26 '16 at 23:00
  • Look at this: http://en.cppreference.com/w/cpp/language/operator_precedence – Logman Jun 26 '16 at 23:01
  • @Logman Precedence doesn't apply to UB (see: sequence points), which would be the case in C. Is C++ the same beast? *shrug* – user2864740 Jun 26 '16 at 23:01
  • 3
    Please tag as C *or* C++, preferably with the target version (eg. C++03 or C++11, as things change). C and C++ are *different* languages, with further variations between revisions. – user2864740 Jun 26 '16 at 23:03
  • Last link was for C++ for C look at this: http://en.cppreference.com/w/c/language/operator_precedence – Logman Jun 26 '16 at 23:05
  • 2
    @Logman, Operator precedence doesn't define evaluation order or sequencing. – chris Jun 26 '16 at 23:06
  • I have seen this in C code but my current code is C++98 / C++2003 – laserbeamer Jun 26 '16 at 23:09
  • @laserbeamer Then why are you asking about C? – melpomene Jun 26 '16 at 23:09
  • @melpomene because the code is used in a header which is used with both languages. – laserbeamer Jun 26 '16 at 23:12
  • @chris And you can read about it in Notes section of article I provided ;) – Logman Jun 26 '16 at 23:12
  • Possible duplicate of [Why are these constructs (using ++) undefined behavior?](http://stackoverflow.com/questions/949433/why-are-these-constructs-using-undefined-behavior) – Raymond Chen Jun 26 '16 at 23:25

3 Answers3

6

I have searched in the C standard but could not find an answer

The thing you're searching for is the sequence point.

Your expression

c * (t /= d) * t * t + b

doesn't contain any sequence points, so the sub-expressions may be evaluated in any relative order.


NOTE that this applies to C, since you mentioned that in the question. You've also tagged the related-but-very different language C++, which has different rules. Luckily, in this case, they give exactly the same result.

The relevant text from the 2014-11-19 working draft PDF:N4296 is

1.9 Program Execution [intro.execution]

...

14 Every value computation and side effect associated with a full-expression is sequenced before every value computation and side effect associated with the next full-expression to be evaluated.

15 Except where noted, evaluations of operands of individual operators and of subexpressions of individual expressions are unsequenced. [ Note: In an expression that is evaluated more than once during the execution of a program, unsequenced and indeterminately sequenced evaluations of its subexpressions need not be performed consistently in different evaluations. — end note ] The value computations of the operands of an operator are sequenced before the value computation of the result of the operator. If a side effect on a scalar object is unsequenced relative to either another side effect on the same scalar object or a value computation using the value of the same scalar object, and they are not potentially concurrent (1.10), the behavior is undefined. [ Note: The next section imposes similar, but more complex restrictions on potentially concurrent computations. — end note ]

So the logic in C++ is that unless things are explicitly sequenced (eg, by a ; separating two full expressions), then they can happen in any order.

As the (second) highlighted section mentions, when two un-sequenced sub-expressions modify the same object (or one modifies and one reads), the behaviour is undefined.

Useless
  • 64,155
  • 6
  • 88
  • 132
  • "Sequence point" hasn't been in the C++ standard since the 2003 one. I'm not sure which language is actually being used (admittedly probably C), but it won't help as much for C++. – chris Jun 26 '16 at 23:04
  • C++11 : http://stackoverflow.com/questions/9293595/are-there-any-situations-where-code-would-have-a-sequence-point-in-c11-but-not – user2864740 Jun 26 '16 at 23:05
  • I wasn't entirely sure it was in there after '98, but the question was about C. I've added the mandatory caveat since I noticed the C++ tag. – Useless Jun 26 '16 at 23:06
  • 1
    The behavior is *undefined*, not merely in an unspecified order! –  Jun 27 '16 at 11:08
  • Yep, I've highlighted the relevant phrase in the quote. – Useless Jun 27 '16 at 11:47
3

The following statement:

return c * (t /= d) * t * t + b;

invokes undefined behaviour in C (and I believe in C++ too). This is because t is evaluated twice (counting the (t /= d) subexpression) despite of an unsequenced side effect (produced by the compound assignment operator), that is affecting object represented by t variable.

The moment when you encounter UB is the one you should stop thinking about "proper" value of the expression. There is none, because anything is possible, including turning off your PC.

The recent versions of gcc and clang with -Wall may tell you that expression is suspected of invoking UB. Here, the warnings are:

warning: operation on 't' may be undefined [-Wsequence-point]

warning: unsequenced modification and access to 't' [-Wunsequenced]

Grzegorz Szpetkowski
  • 36,988
  • 6
  • 90
  • 137
  • @melpomene: Of course it is. What is the value of `t` after `(t /= d)`? – Grzegorz Szpetkowski Jun 26 '16 at 23:10
  • OK, then I don't understand what an "unsequenced side effect" is. – melpomene Jun 26 '16 at 23:11
  • @melpomene: Unsequenced means, that expression haven't encountered sequence point (here, the `;`), which "clears out" the situation. More technically, all side effects are guaranteed to be finished. – Grzegorz Szpetkowski Jun 26 '16 at 23:13
  • 3
    There's still nothing wrong with reading a variable twice. The problem is not that `t` is read twice, it's that `t` is both read and written without an intervening sequence point. I don't think "within" makes sense there either. – melpomene Jun 26 '16 at 23:17
  • @melpomene: I clarified my answer a bit. Essentially, I say something like that `return a++ + a;` is UB, because `a` cannnot be read twice (before the sequence point). – Grzegorz Szpetkowski Jun 26 '16 at 23:22
  • 1
    You can't say "X is between Y". You can't be "between" one other thing, only between two things. – melpomene Jun 26 '16 at 23:27
3

The above expression, with parenthesis making the order of operations explicit, is as follows:

return ((((c * (t /= d)) * t) * t) + b);

The problem here, however, is that there is no sequence point in this expression. So any of the subexpressions can be evaluated in any order.

For example, the compiler may choose to evaluate the value of t once, then use the original value each place it appears. Conversely, it may first evaluate t /= d which modifies t, then use this modified value anyplace else it appears.

In short, because you are both reading and writing a variable in a single expression without a sequence point, you invoke undefined behavior.

dbush
  • 205,898
  • 23
  • 218
  • 273
  • The behavior is undefined **because** the language does not prescribe the order of operations, even with all those parentheses. – Pete Becker Jun 27 '16 at 01:37
  • 1
    @Pete (and dbush too): Don't make the mistake of thinking "undefined behavior" merely means "it's calculated in an unspecified order". –  Jun 27 '16 at 11:11
  • @Hurkyl - I don't see what that has to do with what I said. But thank you for your condescension. – Pete Becker Jun 27 '16 at 11:16
  • @Pete: It has to do with what you said, because what you said makes the mistake of equating undefined behavior with operations happening in an unspecified order. I don't see what your second sentence has to do with what I said. But I appreciate the condescension on your part. –  Jun 27 '16 at 11:25
  • @Hurkyl -- no. Read what I said. Then if you don't understand it, read it again. "The behavior is undefined **because** the language does not prescribe the order of operations...". is a statement about the **reason** for the decision that the behavior of such constructs is undefined, not about the definition of undefined behavior nor about the consequences. – Pete Becker Jun 27 '16 at 11:52
  • @Pete: If the designers simply didn't want to prescribe an order of operations, they would have left the order *unspecified*. –  Jun 27 '16 at 11:57
  • @Hurkyl - that would have been a possibility, but it would preclude aggressive optimizations by the compiler that the current formulation allows. – Pete Becker Jun 27 '16 at 12:02