-2

Consider the following line of code.

int i = 2;
i = i++ 

The second line of code has been identified as undefined. I know this question has been asked before several times and example being this.

But nowhere could I see the issue of operator precedence being addressed in this issue. It has been clearly mentioned that postfix operator precedes assignment operator.

i = (i++)

So clearly i++ will be evaluated first and this value of i is the assigned to i again.

This looks like this particular undefined behavior is contradicting the precedence rule.

Similar to this is the code:

int i = 2;
i++ * i++;

Here according to operator precedence the code can be written as

int i =2;
(i++) * (i++)

Now we do not know whether the (i++) in LHS or RHS of '*' operator is going to be evaluated first. But either way it is going produce the same result. So how is it undefined?

If we write say:

int p;
p = f1() + f2()

where f1() and f2() are defined functions then obviously it's clear we can't decide whether f1() or f2() is going to be evaluated first as precedence rules does not specify this. But a confusion like this does not seem to arise in the current problem.

Please explain.

I do not understand why this question got a negative vote. I needed a clarity between operator precedence and UB and I have seen no other question addressing it.

TARS
  • 47
  • 5
  • `i++` will be evaluated and value will be returned. The UB is arising not from this but from the fact you can't know *when* the value of `i` is going to be *updated* as a result of this operation. – Eugene Sh. Jun 22 '18 at 17:37
  • @EugeneSh.: Why cannot we be sure about it? i++ update the value of i when called independently. Why not update when called in an expression? – TARS Jun 22 '18 at 17:40
  • Because C is not working this way. The side effect of `i++` is guaranteed to be carried out only after a sequence point, but not guaranteed *not* to be carried out before. In Java it will be like you describe (AFAIK) – Eugene Sh. Jun 22 '18 at 17:41
  • does `i` get set to `i` and then the side-effect `i++` takes place, or does the side-effect happen first only for `i` to be set to the value of what `i` was before the side-effect? – Christian Gibbons Jun 22 '18 at 17:43
  • Please don't add extra questions after receiving two comprehensive answers. – Eugene Sh. Jun 22 '18 at 17:58
  • @EugeneSh. So as you said the side effect of i++ which is incrementing the value of i is guaranteed to be carried out after a sequence point and the nearest sequence point is the end of the expression here. So naturally it's only gonna be guaranteed that i will be incremented after the end of expression but may or may not get incremented in between and this is the root cause of the UB here. Am I right? – TARS Jun 22 '18 at 20:21
  • Yes.I think the answers are addressing it. – Eugene Sh. Jun 22 '18 at 20:46

4 Answers4

4

What you're looking for is in section 6.5 on Expressions, paragraph 3 of the C standard:

The grouping of operators and operands is indicated by the syntax. Except as specified later, side effects and value computations of subexpressions are unsequenced.

This means that the side effect of incrementing (or decrementing) via the ++ or -- operators doesn't necessarily happen immediately when the operator is encountered. The only guarantee is that it happens before the next sequence point.

In the case of i = i++;, there is no sequence point in the evaluation of the operands of = nor in the evaluation of postfix ++. So an implementation is free to perform assigning the current value of i to itself and the side effect of incrementing of i in any order. So i could potentially be either 2 or 3 in your example.

This goes back to paragraph 2:

If a side effect on a scalar object is unsequenced relative to either a different side effect on the same scalar object or a value computation using the value of the same scalar object, the behavior is undefined.

Since i = i++ attempts to update i more than once without a sequence point, it invokes undefined behavior. The result could be 2 or 3, or something else might happen as a result of optimizations for example.

The reason that this is not undefined:

int p;
p = f1() + f2()

Is because a variable is not being updated more than once in a sequence point. It could however be unspecified behavior if both f1 and f2 update the same global variables, since the evaluation order is unspecified.

dbush
  • 205,898
  • 23
  • 218
  • 273
  • You said that the evaluation of operands of '=' has no sequence point. But isn't there an implicit sequence point in evaluation of operands of '=' ? The LHS of '=' operator is guaranteed to be an l-value. If we write say int a = f(), the operands are a and f(). Now isn't it guaranteed here that f() will be evaluated first? Can you cite an example where the LHS of the assignment operator could be evaluated first leading to UB? – TARS Jun 22 '18 at 20:30
  • @TARS You're correct that the right hand side of an assignment must be evaluated before being assigned to the left side. However, the left side needs to be evaluated as well. For example if `a` is an array, then `a[f()]` is an lvalue. That needs to be evaluated before it can be assigned to. So in an expression like `a[f()] = g();` whether `f` or `g` is run first is unspecified. – dbush Jun 22 '18 at 20:34
  • @TARS And that still doesn't touch on side effects such as those caused by `++` or `--`. – dbush Jun 22 '18 at 20:37
  • I wish to get one more point clear. Say we consider the example you gave here, **a[f()] = g()**. Let us say g() gets evaluated first and suppose g() modifies some global variable **int a** and finally it returns some other variable **int c**. Now we are guaranteed that all the body of the function will be done before it returns the final value. In general all the side effects will be updated before the value is returned. So with the same logic here why is it that the value is returned by i++ but the side effect of incrementing the value i may not be done? – TARS Jun 22 '18 at 21:02
  • @TARS Calling a function creates a sequence point. Using `++` does not. – dbush Jun 22 '18 at 21:04
  • Okay got it. Thanks – TARS Jun 22 '18 at 21:13
2

The problem with using

i = i++ 

is that the order in which the address of i is accessed to read and write is not specified. As a consequence, at the end of that line, the value of i could be 3 or 2.

When will it be 3?

  1. Evaluate the RHS - 2
  2. Assign it to the LHS. i is now 2.
  3. Increment i. i is now 3.

When will it be 2?

  1. Evaluate the RHS - 2
  2. Increment i. i is now 3.
  3. Assign the result of evaluating the RHS to the LHS. i is now 2.

Of course, if there is a race condition, we don't know what's going to happen.

R Sahu
  • 204,454
  • 14
  • 159
  • 270
1

But nowhere could I see the issue of operator precedence being addressed in this issue.

Operator precedence only affects how expressions are parsed (which operands are grouped with which operators) - it has no effect on how expressions are evaluated. Operator precedence says that a * b + c should be parsed as (a * b) + c, but it doesn't say that either a or b must be evaluated before c.

Now we do not know whether the (i++) in LHS or RHS of '*' operator is going to be evaluated first. But either way it is going produce the same result. So how is it undefined?

Because the side effect of the ++ operator does not have to be applied immediately after evaluation. Side effects may be deferred until the next sequence point, or they may applied before other operations, or sprinkled throughout. So if i is 2, then i++ * i++ may be evaluated as 2 * 2, or 2 * 3, or 3 * 2, or 2 * 4, or 4 * 4, or something else entirely.

John Bode
  • 119,563
  • 19
  • 122
  • 198
0

OPERATOR PRECEDENCE DOES NOT RESOLVE UNDEFINED EXPRESSION ISSUES.

Sorry for shouting, but people ask about this all the time. I'm afraid I have to say your research on this question must not have been very thorough, if you didn't see this aspect being discussed.

See for example this essay.

The expression i = i++ tries to assign to object i twice. It's therefore undefined. Period. Precedence doesn't save you.

Steve Summit
  • 45,437
  • 7
  • 70
  • 103
  • I didn't have time to run through research papers and I only looked in stack overflow for this question and I couldn't find any article which differentiates it. Also I did not expect the rules to change just because I asked about it. Just a process of learning is all. – TARS Jun 22 '18 at 22:35