views:

376

answers:

2

Why does the following compile in C++?

int phew = 53;
++++++++++phew ;

The same code fails in C, why?

+14  A: 

That is because in C++ pre-increment operator returns an lvalue and it requires its operand to be an lvalue.

++++++++++phew ; in interpreted as ++(++(++(++(++phew))))

However your code invokes Undefined Behaviour because you are trying to modify the value of phew more than once between two sequence points.

In C, pre-increment operator returns an rvalue and requires its operand to be an lvalue. So your code doesn't compile in C mode.

Prasoon Saurav
@Prasoon: Not to second guess you, I am just curious to read about what you said; "you are trying to modify the value of phew more than once between two sequence points". Can you provide an annotation to this part of the standard so I can read more about this?
Merlyn Morgan-Graham
@Merlyn Morgan-Graham : Read this article by Steve Summit: http://c-faq.com/expr/seqpoints.html .
Prasoon Saurav
From The New C Standard (An Economic and Cultural Commentary): `Section 3.1``NOTE 1` Where only one of these two actions is meant, “read” or “modify” is used.`NOTE 2 `“Modify” includes the case where the new value being stored is the same as the previous value.modify includes cases.
Prasoon Saurav
`Commentary`This specification only needs to be followed exactly when there is more than one access between sequencepoints, or for objects declared with the volatile storage class. As far as the developer is concerned, amodification occurs. As long as the implementation delivers the expected result, it is free to do whatever itlikes.
Prasoon Saurav
Just to be clear, `++++i` is well-defined for user-defined types, right?
FredOverflow
@FredOverflow: Yes because function call introduces a sequence point. :)
Prasoon Saurav
I cannot quote the standard, but I believe that the behavior is well defined. The standard requires an specific order of execution. Consider that you had `int }` and the call: `int x = 0; f( f( f( x ) ) );` The inner `f` must be executed *before* the middle `f` and that is guaranteed to be performed before the external `f` call. The semantics if pre-increment *require* the increment to be performed before the value is yielded. On the other hand, `int x = 0; int y = ++x + x++;` is undefined as the relative order of execution of the pre and post increment undefined.
David Rodríguez - dribeas
@David: §5/4: "Between the previous and next sequence point a scalar object shall have its stored value modified at most once by the evaluation of an expression." I think Prasoon is right, although it would almost be hard to write a compiler so bad as to get it wrong.
Potatoswatter
@Potatoswatter: I'd guess that the simple mistake you could make in writing the compiler would be to inline the `operator++`, and then to apply optimisations to the result as if it were a single expression, without somehow annotating the inlined code to retain the sequence point. It's a fairly fundamental error, since if you got it wrong in this case I bet the optimiser would incorrectly coalesce/reorder statements in other cases too, but I think it's an omission rather than a comission...
Steve Jessop
This is only UB in C++03 though. It's valid in C++0x.
Johannes Schaub - litb
@Johannes: What is changing in C++0x that makes this no longer UB?
Daniel Trebbien
@Daniel see my answer for an explanation
Johannes Schaub - litb
+8  A: 

Note: The two defect reports DR#637 and DR#222 are important to understand the below's behavior rationale.


For explanation, in C++0x there are value computations and side effects. A side effect for example is an assigment, and a value computation is determining what an lvalue refers to or reading the value out of an lvalue. Note that C++0x has no sequence points anymore and this stuff is worded in terms of "sequenced before" / "sequenced after". And it is stated that

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, the behavior is undefined.

++v is equivalent to v += 1 which is equivalent to v = v + 1 (except that v is only evaluated once). This yields to ++ (v = v + 1) which I will write as inc = inc + 1, where inc refers to the lvalue result of v = v + 1.

In C++0x ++ ++v is not undefined behavior because for a = b the assignment is sequenced after value computation of b and a, but before value computation of the assignment expression. It follows that the asignment in v = v + 1 is sequenced before value computation of inc. And the assignment in inc = inc + 1 is sequenced after value computation of inc. In the end, both assignments will thus be sequenced, and there is no undefined behavior.

Johannes Schaub - litb
Good answer. Similarly `int a=4; ++a=5;` would not invoke UB in C++0x, right?
Prasoon Saurav
@Prasoon yes. It would set `a` to 5. Likewise `a = ++a;` will not be UB, but `a = a++;` will be UB.
Johannes Schaub - litb
@Johannes : Cool :-). The world is changing.
Prasoon Saurav
@Johannes : What about `int a=4,b; b = a + a++ ;`, that would be UB or not. Lets see, addition of `a` and `a++` is sequenced before assignment, but hey order of evaluation is still unspecified, is it?.Phew! this is confusing.
Prasoon Saurav
@Johannes: I don't understand why `a = ++a` does not invoke undefined behavior, but `a = a++` does. What happened to "except where noted, the order of evaluation of operands of individual operators and subexpressions of individual expressions, and the order in which side effects take place, is unspecified"?
Daniel Trebbien
@Daniel, the second does invoke undefined behavior because the modification to a in "a++" is not sequenced before, but *after* the value computation of "a++" (naturally, because you want it to yield the old value). Thus the assignment and the modification in "a++" is not sequenced relative to each other. The text you quoted is now worded as "Except where noted, evaluations of operands of individual operators and of subexpressions of individual expressions are unsequenced."
Johannes Schaub - litb
@Prasoon, the modification to a in "a++" is sequenced after value computation of "a++". The two a's in "a + a++" are not sequenced in any way, neither their value computations, nor their side effects. So you have a side effect (modification to a) that is unsequenced relative to a value computation to that a (the first operand), and so you have UB.
Johannes Schaub - litb
@Johannes : I see, thanks.
Prasoon Saurav
@Johannes: Thanks for clarifying. I understand now.
Daniel Trebbien
a = a++; the steps involved are a) value computation of lvalue of 'a' (lhs) b) value computation of lvalue of 'a' (rhs), c) side effect of ++, d) side effect of assignment. Do you mean that in C++0x steps (d) and (c) are unsequenced?
Chubsdad
But given a = ++a, since (c) happens before (d), the two side effects are sequenced on the same scalar i.e. 'a' and hence the expression is well-formed?
Chubsdad
@Chubsdad you are right that's what i mean.
Johannes Schaub - litb
Two clarifications: Value computation of assignment operator includes steps (a), (b) and (c). (d) is a side effect of assignment operator.
Chubsdad
and the second clarification: 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, the behavior is undefined. - The example discussed above is about side effects being unsequenced with respect to another side effect. Is there an example of 'side effect being unsequenced with respect to value computation using the value of the same scalar object (the second 'or' clause in the statement)
Chubsdad
@Chubsdad For the first one, note that value computation alone does not *imply* side effects or such (see 1.9/12 - it's "Evaluation" that in generally does). It's just that for an assignment expression, its value computation is sequenced after (c), (a) and (b) (the "sequenced before" relation is transitive).
Johannes Schaub - litb
For your second clarification - this was phrased in C++03 using the slender "Furthermore, the prior value shall be accessed only to determine the value to be stored.", which was hardly decisive. C++0x replaces it with what you quoted, and includes orders where necessary. For instance in `a++`, the previous value of "a" is accessed to determine the value being stored, so C++0x induces an order into this. But for "a + a++", this is not the case with respect to the first "a", so it's not ordered and it becomes undefined. This shows a strong point of the C++0x wording. It's much more reliable.
Johannes Schaub - litb
@Johannes : Ohh boy! `:)`
Prasoon Saurav
@Daniel please consider my new answer with the two DR links. They will show that both `a = ++a` and `++ ++ a` is defined behavior and is intended to be so.
Johannes Schaub - litb
@Prasoon update ^^
Johannes Schaub - litb
@Chubsdad update ^^ (found two DRs that clarify this and support this answer).
Johannes Schaub - litb
@Johannes : Excellent find. You should probably show `DR637` to `James Kanze` who made all of us(excluding you) think that he was right all the time. `:-)`
Prasoon Saurav