0

I know that:

int b = 1, c = 2, d = 3, e = 4;
printf("%d %d %d", ++b, b, b++);

results in undefined behavior. Since

Modifying any object more than once between two sequence points is UB. Undefined behavior and sequence points

But I don't know if:

int b = 1, c = 2, d = 3, e = 4;
printf("%d", b++ + ++c - --d - e--);

is also UB?

What I think is that increment/decrement operators will evalute first because of the precedence, between them right to left since the associativity . Then arithmetic operators will be evaluated left to right.

Which will just be

(b) + (c + 1) - (d - 1) - (e)

that is, 1 + (2 + 1) - (3 - 1) - (4)

= (2 - 4)

= -2

Is it right?

pmg
  • 106,608
  • 13
  • 126
  • 198
bedirhan
  • 21
  • 2
  • 3
    It results in a compilation error. If you fix the quotes and put it in a function then it's fine because each variable is only accessed in one place. – interjay Jun 28 '21 at 11:55
  • It's not really a precedence issue, but rather an evaluation issue. – Some programmer dude Jun 28 '21 at 12:00
  • 1
    The precedence rules do not matter. The value of `b++ + ++c - --d - e--` is the "sum" of values `b++`, `++c`, `--d`, and `e--`. The value of `b++` is the value of `b` at the last sequence point, the value of `++c` is `1` more than the value of `c` at the last sequence point, .... (the side effect of updates to variables within sequence points does not change these values) – pmg Jun 28 '21 at 12:01
  • Why would you think the second code could be UB? – Support Ukraine Jun 28 '21 at 12:04
  • You can try it and modern compilers will give a warning. For GCC it is with `-Wsequence-point` and for clang with `-Wunsequenced` (both are enabled by `-Wall`, which you should be used anyway). – Simon Doppler Jun 28 '21 at 12:05
  • I tried using `-Wall`, but the second code didn't give any warning. – bedirhan Jun 28 '21 at 12:12
  • See [What is the difference between operator precedence and order of evaluation?](https://software.codidact.com/posts/278172). – Lundin Jun 28 '21 at 12:22

2 Answers2

7

But I don't know if: ... is also UB?

It is not, but your reasoning about why is fuzzy.

What I think is that increment/decrement operators will evaluate first because of the precedence, between them right to left since the associativity . Then arithmetic operators will be evaluated left to right.

Precedence determines how the result is calculated. It doesn't say anything about the ordering of the side-effects.

There is no equivalent of precedence telling you when the side effects (the stored value of b has been incremented, the stored value of e has been decremented) are observable during the statement. All you know is that the variables have taken their new values before the next statement (ie, by the ;).

So, the reason this is well-defined is that it does not depend on those side-effects.


I deliberately hand-waved the language to avoid getting bogged down, but I should probably clarify:

  • "during the statement" really means "before the next sequence point"
  • "before the next statement (... ;)" really means "at the next sequence point"

See Order of evaluation:

  1. There is a sequence point after the evaluation of all function arguments and of the function designator, and before the actual function call.

So really the side-effects are committed before the call to printf, so earlier than the ; at the end of the statement.

Useless
  • 64,155
  • 6
  • 88
  • 132
  • I believe the side-effects will have taken place before the function (`printf`) is called. That's not relevant here but a called function may have a way of accessing variables modified during the statement. – Persixty Jun 28 '21 at 12:18
  • 1
    Yeah, I elaborated on that - I'd tried to avoid including the sequence point rules to just focus on the confusion between operator precedence and order of evaluation. – Useless Jun 28 '21 at 12:26
  • I really like the answer. The order of precedence vs evaluation is a great cause of confusion and you explain it well. That's why I decided to be pedantic about your answer rather than offer my own. – Persixty Jun 28 '21 at 12:40
0

There is a gigantic difference between the expressions

b++ + ++c - --d - e--

(which is fine), and

x++ + ++x - --x - x--

(which is rampantly undefined).

It's not using ++ or -- that makes an expression undefined. It's not even using ++ or -- twice in the same expression. No, the problem is when you use ++ or -- to modify a variable inside an expression, and you also try to use the value of that same variable elsewhere in the same expression, and without an intervening sequence point.

Consider the simpler expression

++z + z;

Now, obviously the subexpression ++z will increment z. So the question is, does the + z part use the old or the new value of z? And the answer is that there is no answer, which is why this expression is undefined.

Remember, expressions like ++z do not just mean, "take z's value and add 1". They mean, "take z's value and add 1, and store the result back into z". These expressions have side effects. And the side effects are at the root of the undefinedness issue.

Steve Summit
  • 45,437
  • 7
  • 70
  • 103