The result of int c=0; cout<<c++<<c;
Asked Answered
I

5

5

I think it should be 01 but someone says its "undefined", any reason for that?

Incursive answered 8/4, 2010 at 20:42 Comment(0)
A
8

c++ is both an increment and an assignment. When the assignment occurs (before or after other code on that line) is left up to the discretion of the compiler. It can occur after the cout << or before.

This can be found in the C99 standard http://www.open-std.org/JTC1/SC22/wg14/www/docs/n1124.pdf

You can find it on page 28 in the pdf or section 5.1.2.3

the actual increment of p can occur at any time between the previous sequence point and the next sequence point

Since someone asked for the C++ standard (as this is a C++ question) it can be found in section 1.9.15 page 10 (or 24 in pdf format)

evaluations of operands of individual operators and of subexpressions of individual expressions are unsequenced

It also includes the following code block:

i = v[i++]; // the behavior is undefined
i = 7, i++, i++; // i becomes 9
i = i++ + 1; // the behavior is undefined

I feel that the C99 standard's explanation is clearer, but it is true in both languages.

Astromancy answered 8/4, 2010 at 20:47 Comment(3)
Is it just me, or is quoting from the C99 Standard not a great answer to a C++ question?Bonham
The sequence point doesn't have to be a semicolon. A function call is a sequence point too (see Annex C of the standard). The problem is there is no sequence point between the evaluation of parameters c++ and c.Silberman
@Neil I added the C++ standard re: ISO/IEC 14882 @Silberman in the example they use the next sequence point happens to be a semicolon which is what the quote was referring to, since I removed the context it may seem ambiguous, but it is grammatically accurate. I'll remove the peren statementAstromancy
C
4

It is undefined behavior if you modify a value and then read it (or try to modify it again) without an intervening sequence point. The concept of a sequence point in C++ is a bit technical (you can read a little about it here), but the bottom line is that stream insertion (<<) is not a sequence point.

The reason why this is undefined behavior is because, in the absence of a sequence point, the compiler is allowed to re-order operations in any way it sees fit. That is, it is permitted to retrieve the value of c (and hold onto it for the second insertion) and then afterwords execute c++ to get the value for the first insertion. So you can't be sure whether the increment will occur before or after the value of c for the second insertion is determined.

Color answered 8/4, 2010 at 20:50 Comment(1)
And remember that changing the value of a variable twice between sequence points, or changing the value and accessing the value in any way not directly connected with changing it between sequence points, is undefined. According to the Standard, anything can happen.Wield
I
1

The behavior is defined but unspecified. The relative order of evaluating the two uses of 'c' in the expression isn't specified. However, if you convert it to functional notation, it looks like this:

cout.operator<<(c++).operator<<(c);

There's a sequence point between evaluating the arguments to a function, and executing the body of the function, and function bodies aren't interleaved, so the result is only unspecified, not undefined behavior.

If you didn't have an overloaded operator:

int c=0;
int a = c++ << c;

Then the behavior would be undefined, because both modifying and using the value of c without an intervening sequence point.

Edit: The sequence litb brings up is simply wrong. The standard specifies (§1.9/17): "When calling a function (whether or not the function is inline), there is a sequence point after the evaluation of all function arguments (if any) which takes place before execution of any expressions or statements in the function body."

This clearly written with the idea that arguments are evaluated, then (immediately afterward) the body of the function is executed. The sequence he suggests, in which arguments to one function are evaluated, then arguments to another, then execution of both function bodies doesn't seem to have been intended, but also isn't prohibited. That, however, changes nothing -- the requirement is still that: "...there is a sequence point after the evaluation of all function arguments (if any)..."

The subsequent language about execution of the body does NOT remove the requirement for a sequence point after evaluating all function arguments. All other evaluation, whether of the function body or other function arguments follows that sequence point. I can be as pedantic and perverse as anybody about mis-reading what's clearly intended (but not quite stated) -- but I can't imagine how "there is a sequence point after the evaluation of all function arguments" can be read as meaning "there is NOT a sequence point after the evaluation of all function arguments."

Neil's point is, of course, correct: the syntax I've used above is for member functions. For a non-member overload, the syntax would be more like:

operator<<(operator<<(cout,c++), c);

This doesn't remove the requirement for sequence points either though.

As far as it being unspecified: it's pretty simple really: there's a sequence point after evaluating all function arguments, so all the arguments for one function call must be fully evaluated (including all side effects), then arguments for the other function call can be evaluated (taking into account any side effects from the other) -- BUT there's no requirement about WHICH function call's arguments must be evaluated first or second, so it could be c, then c++, or it could be c++, then c -- but it has to be one or the other, not an interleaving.

Interrogation answered 8/4, 2010 at 20:48 Comment(32)
Behavior is undefined for the shown code. The function-entry sequence point has to occur before entering the function and after evaluating its arguments. A compiler that evaluates both c++ and c without an intervening sequence point is fully conforming.Exteriorize
@Jerry Code only true if op<<() is a member function.Bonham
Can't c and c++ both be evaluated before the sequence point for the first function call? In this case there would be no intervening sequence point and undefined behaviour.Diminution
@Neil: Why does it matter if op<< is a member function?Diminution
@Charles I meant to point out that the syntax would not be x.y(z), it would be y(x,z).Bonham
In other words, this is how stuff can be evaluated: [A = eval(c++), B = eval(c)] # [operator<<(A)] # [operator<<(B)] (those hashes shall signify that there is a sequence point).Exteriorize
@Johannes: So the key here is Section 5 Clause 4: "...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."? (The rest of the clause expresses that modifying and reading a scalar variable is undefined behavior, which we all know.)Monanthous
It's similar to this, but not the same because of the possibility of UB: #2129730Diminution
@Charles: It matters for the translation Jerry does. However, operator << (int) is a member function of basic_ostream (see the original C++ standard, section 27.6.2.1), so it does apply here. However, I believe (but am not certain that) Johannes is correct.Wield
Oh, and I don't see how this could possibly be unspecified. Either there is a sequence point between the two uses of c, in which case it's defined behavior, or there isn't, and it's undefined behavior. I need to do some more careful reading about sequence points.Wield
@David: I see that it matters for the translation, when I first read Neil's comment I thought "only if" applied to the overall conclusion.Diminution
@Jerry So in operator<<(operator<<(cout,c++), c) (or in your original) where exactly IS the sequence point that prevents this from being UB?Bonham
@Neil: see my edit, quoting the relevant language from the standard.Interrogation
@Jerry I was hoping you would point out the seq point in the actual example .Bonham
OK, so there are two parameters to the outer op<<. Evaluating right to left (as one option), I start by evaluating c, then move on to op<<(cout, c++). OK, so I haven't reached either function sequence point yet as I move on to the parameters of op<<(cout, c++). Right to left, I evaluate c++ but I still haven't reached either sequence point implied by the function calls. Surely this is UB?Diminution
@Jerry you haven't provided us with a quotation for that. In particular, you need a quotation that argument evaluation of individual function calls within an expression doesn't interleave. However you just quoted that arguments are evaluated before any evaluation that happens inside the called functions (which is crucial, of course).Exteriorize
For reference, this is the C++0x text: "When calling a function (whether or not the function is inline), every value computation and side effect associated with any argument expression, or with the postfix expression designating the called function, is sequenced before execution of every expression or statement in the body of the called function.". No sign of "arguments are evaluated, then (immediately afterward) the body of the function is executed" even in this revised text.Exteriorize
"The sequence he suggests, in which arguments to one function are evaluated, then arguments to another, then execution of both function bodies doesn't seem to have been intended, but also isn't prohibited" <- actually, if it isn't prohibited, then it is allowed by unspecified behavior, and then your program has undefined behavior if it relies on it.Exteriorize
@litb:so your position is that "...there is a sequence point after the evaluation of all function arguments..." means "...here is not a sequence point after the evaluation of all function arguments..."? If there IS a sequence point after the evaluation of all function arguments, how can anything other than evaluation of those arguments happen without that intervening sequence point preventing undefined behavior?Interrogation
If i say "there is a number N greater than 1 which is smaller than 100", i don't say that this number is 2. But you seem to imply that.Exteriorize
@Charles:you're making a fundamental mistake -- 'op<<cout,c)' isn't really the argument to the outer function -- the reference to cout that it returns is the argument. As such, it can evaluated [cout,c] then [cout, c++], or it can evaluate [cout, c++], then [cout, c], but either way there's a sequence point between the two.Interrogation
@litb:where do you come up with nonsense like that? Let's get to a simple yes or no question: do you agree that there is a sequence point after evaluating all arguments to a function?Interrogation
@Jerry in my proposed sequence there is a sequence point after c++ happening before the first op<<, and there was a sequence point after c also happening before the second op<<. So yes, i agree. Also i don't think it's nonsense. Here are the semantics: 1 = <evaluation of args of 1st "op<<">, N = <sequence point>, 100 = <evaluation of function body>. Then, you say "ah, N must be 2", but i say "N can be 50, and the numbers in between can represent other evaluations in that expression, because 50 is less than 100 and greater than 1".Exteriorize
How is op<<(cout,c++) not the first argument in the 'outer' op<< ? The fact that it evaluates to cout (via and lvalue reference return value) - if indeed it does - is an implementation detail. You seem to allow that c can be evaluated before c++ but there are only two sequence points for the functions and I can't see how either of them (in this evaluation order) can occur between the evaluation of c and c++.Diminution
I don't know whether entering in this discussion, but I guess I will. in f( g( a, b ), c ) the standard states that evaluation of a and b must happen before g is evaluated, and that evaluation of both g and c must occur before evaluation of f. But that is only a partial order. The evaluation order can be: c, b, a, g, f, or b, c, a, g, f, or b, a, g, ... you follow. Now, if you replace f == g == operator<<, a == cout, b == c++, c == c (last one was easy!) there are different combinations that allow c++ to be executed before or after cPaleozoic
@dribeas: And do you think that there are orders for which there is no sequence point between the evaluation of b and c?Diminution
Also, in f( g(a, b), c, d ) the order can be a d b # g # c # f #. Of course, this time the sequence point is quite "immediately afterwards" evaluating g's arguments. But as can be seen in this example, this doesn't quite forbid interleaving evaluations of other subexpressions.Exteriorize
Isn't the issue that Jerry is reading "here is a sequence point after the evaluation of all function arguments (if any)..." but IGNORING "which takes place before execution of any expressions or statements in the function body" - in other words there is no SP following evaluation - the SP is before execution.Bonham
@litb:I'm starting to understand your position, but it still seems (to me) to require violation of another clause: §1.9/8, which requires that: "Once the execution of a function begins, no expressions from the calling function are evaluated until execution of the called function has completed." To get your sequence, we have to treat evaluating the arguments to the function as not being part of "execution of the function". I suppose that's barely possible, but it seems like quite a stretch.Interrogation
@Neil:I'm not really ignoring that -- but I'm also paying attention to 1.9/8, which basically says execution of a function is atomic (and at least as I'd read it, evaluating the arguments is part of executing the function).Interrogation
@Jerry, that's not a stretch at all. Execution of a function does not include evaluation of its arguments. The arguments are part of the full expressions of the call, and as such are evaluated as part of the execution of the caller. The execution of a function begins when the first statement in its body is reached.Exteriorize
@litb:what backing do you have for that claim? Take careful note of the difference between 1.9/17 and 1.9/8: /8 talks about execution of the function, while /17 specifies execution of the body of the function -- but as you're reading it, that distinction seems to be completely lost.Interrogation
V
1

The reason it is undefined is that the compiler is free to calculate function parameters in any order. Consider if you where calling a function (because you are, but it's easier to envision when it's in function syntax):


  cout.output(c++).output(c);

The compiler may hit the parameters in reverse order, forward order, or whatever. It may call the first output before calculating the parameter to the second output or it may do both and then call.

Valeriavalerian answered 8/4, 2010 at 20:48 Comment(0)
B
0

As I see it, f(c++); is equivalent to: f(c); c += 1;

And f(c++,c++); is equivalent to: f(c,c); c += 1; c += 1;

But it may be the case that f(c++,c++); becomes f(c,c+1); c+= 2;

An experiment with gcc and clang, first in C

#include <stdio.h>

void f(int a, int b) {
    printf("%d %d\n",a,b);
}

int main(int argc, char **argv) {
    int c = 0;
    f(c++,c++);

    return 0;
}

and in C++

#include <iostream>

int main(int argc, char **argv) {
    int c = 0;
    std::cout << c++ << " " << c++ << std::endl;
    return 0;
}

Is interesting, as gcc and g++ compiled results in 1 0 whereas clang compiled results in 0 1

Bastien answered 5/12, 2013 at 21:40 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.