bedirhan
bedirhan

Reputation: 21

Does this C code result in Undefined Behavior?

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?

Upvotes: 0

Views: 162

Answers (2)

Steve Summit
Steve Summit

Reputation: 47915

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.

Upvotes: 0

Useless
Useless

Reputation: 67703

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.

Upvotes: 7

Related Questions