> Innocent as I am, I expected to see 5 here, but > I get 6 or 4 depending on the compiler. Is this a > case of some "for hard-core c programmers only" > precedence rules?

Modifying the same variable twice in the same expression usually causes undefined behavior. This includes modifications using operators like -- and ++ and += and -=, as well as the plain assignment =. "Hard-core C programmers" will know that there are some cases where it's permissible to do it (due to sequence points), but innocents would do best to avoid trying it. Even among those who know when it's legal would do best to avoid it, in most cases, on the grounds that it tends to make your code incomprehensible.

And that is all you really need to know, but if you really want the nitty-gritty, read on.

If the expression (t=2.0, t) was taken alone, the assignment would be guaranteed to be completed before the right-hand t was evaluated, because the comma operator creates a sequence point. So the value of the expression would be t. And, of course, similarly for the other subexpression.

But the + operator does not cause a sequence point; its operands and their subexpressions can be evaluated in any order, except as constrained by whatever operators that they contain. In par- ticular, evaluation of the two operands of + can be interleaved.

This means that your compiler can execute t=2.0 first, then t=3.0, then go back and evaluate the t after t=2.0, then the other t. That's how you get x = 6. Similarly, if t=3.0 and t=2.0 are exe- cuted in that order before anything else happens, you get x = 4.

Now you might think that the comma operator would protect you from this, but it doesn't. All that it cares about is making sure that the t=2.0 happens *before* the t that comes after the comma; it need not be *immediately* before.

Or you might think that the parentheses would protect you. Wrong again. Parentheses in C expressions have a syntactic role only. That is, they tell the compiler which subexpressions are operands of which operator (and of course they have other syntactic jobs, such as identifying function calls); but they *do not affect order of evaluation*.

At this point you might think that you now understand that x must be 4, 5, or 6, but even this is not guaranteed. The behavior is actually undefined; as far as the standard is concerned, x could be -0.00031416, or your program could abort. Why? Because there's a specific rule in the standard against modifying the same variable twice without a sequence point intervening; if you do, the standard says the behavior is undefined.

So if the subexpressions t=2.0 and t=3.0 are evaluated first (in either order), then the two sequence points caused by the comma operators can come after that, one after the other. They come too late to occur between the two subexpressions, and the rule applies.

See also questions 3.1 through 3.9 in the FAQ list.

And if your head is now spinning: just forget all this detail and go back to the first paragraph. Don't write code like this in the first place, and you won't have a problem.

(Posted and emailed.) -- Mark Brader | "I always pass on good advice. It's the only thing Toronto | to do with it. It is never any use to oneself."

> Innocent as I am, I expected to see 5 here, but > I get 6 or 4 depending on the compiler. Is this a > case of some "for hard-core c programmers only" > precedence rules?

Your code invokes undefined behavior, because it modifies the value of t and takes its value for a purpose other than generating the new value, without an intervening sequence point.

There is a sequence point between the two expressions inside each parenthesized pair, but there is no sequence point between accessing the value of t after the comma in the first pair, and the assignment of 3.0 to t in the second pair. The + operator does not create a sequence point.

Since the behavior is undefined, neither compiler is wrong.

Jack Klein -- Home: http://jackklein.home.att.net --

Mon, 08 Jul 2002 03:00:00 GMT

Hans-Bernhard Broeke#4 / 12

order of evaluation question

Quote:

> x = (t=2.0, t) + (t=3.0, t);

[...]

Quote:

> Innocent as I am, I expected to see 5 here, but I get 6 or 4 > depending on the compiler. Is this a case of some "for hard-core c > programmers only" precedence rules?

No, it's a case of undefined behaviour, I think. I.e. the compiler is allowed to do *anything*, with this source code. It could generate a program that asks you for a glass of vodka, or whatever.

At least, that's what my understanding of the side effect and sequence point rules suggests the answer to be... --

Even if all the snow were burnt, ashes would remain. --

Mon, 08 Jul 2002 03:00:00 GMT

Douglas A. Gwy#5 / 12

order of evaluation question

Quote:

> x = (t=2.0, t) + (t=3.0, t); > Innocent as I am, I expected to see 5 here, but > I get 6 or 4 depending on the compiler. Is this a > case of some "for hard-core c programmers only" > precedence rules?

Surely this must be in the C FAQ. The simple fact is that C implementations can interleave the processing among the parenthesized terms in the above expression. If you want to force an assignment to actually occur before accessing the value of the variable, make sure there is a sequence point between the two operations. The easiest way to do that is to use semicolons and a temporary variable: { register double old_t; t = 2.0; old_t = t; t = 3.0; x = old_t + t; } --

This depends on the order of evaluation or the left-hand and right-hand sides of the + operator. This is allowed to be either one. And, as you found out, depends on the compiler and sometimes a few other circumstances.

Quote:

> printf("x = %f\n", x); > return 0; > }

> Innocent as I am, I expected to see 5 here, but > I get 6 or 4 depending on the compiler. Is this a > case of some "for hard-core c programmers only" > precedence rules?

Order of evaluation problem in a statement with multiple side-effects. Simply a very bad construct ...

> x = (t=2.0, t) + (t=3.0, t); ... > Innocent as I am, I expected to see 5 here, but > I get 6 or 4 depending on the compiler. Is this a > case of some "for hard-core c programmers only" > precedence rules?

Expressions with multiple side affects (changes which last beyond the scope of the expression, such as variable assignments) to the same object result in undefined behavior; it would be perfectly reasonable for your program, under these conditions, to crash (though obviously it's more likely that it simply won't do what you expect).

The issue is that the compiler is under no obligation to evaluate the subexpressions in the order that you expect it to, for purposes of optimization.

No, this is a case of two changes to 't' between adjacent sequence points. The starting sequence point is the entry of main, the finishing sequence point is the end of the assignment statement. Between these, only one assignment to each value may occur. Since you write twice to 't', you invoke undefined behavior. The compiler may ignore either write, use the value of t beforehand, afterwards, or let the computer catch fire. It is impossible to predict what happens when undefined behavior occurs.

>Innocent as I am, I expected to see 5 here, but >I get 6 or 4 depending on the compiler. Is this a >case of some "for hard-core c programmers only" >precedence rules?

Nothing to do with precedence rules but something to do with sequence points. While yje comma operator requires that its left operand be completely evaluated and side effects completed before starting evaluation of the right operand there is no such requirement for +. The result is that the compiler can legitimately evaluate t=2.0 and t=3.0 and the two evaluations of t in any order it likes as long as the above constraint applies. As it is possible to evaluate both the assignments before either of the others the results are strictly undefined. In the cases you tested this did no more harm than shake up your thinking. I guess one compiler is evaluating t=3 first and the other is evaluating t=2. You are lucky that neither compiler did what you expected because now you know about this kind of problem.

Actually, it's undefined behavior. The compiler is free to generate a program that does either of those things, calculate pi to a billion decimal places, spout profanity, or just plain old crash. Side effects to the same object (in this case, t) without an intervening sequence point (think: statement, though there are other types of sequence points such as initializations) results in undefined behavior. A compiler is free to generate a program which exhibits _any_ kind of behavior.