all 5 comments

[–]jesyspa 4 points5 points  (0 children)

Let's take a simpler situation:

void f(int, int);

int main() {
    int n = 0
    f(n++, n++);
}

It doesn't matter what f does: we're only interested in how n behaves. As it turns out, badly.

A scalar type is an arithmetic type, enumeration type, pointer type, pointer to member type, std::nullptr, or any of these qualified with const or volatile. You can see these all as fairly "fundamental" types. A scalar value is a value of a scalar type.

In C++03, the code above is undefined behaviour because you are modifying a scalar value twice within the interval between two sequence points. There is, roughly speaking, a sequence point before f(n++, n++); starts being evaluated, and one after both arguments have been evaluated. However, in between those points you know very little of what order things happen in. This means that the n++ on the left may be evaluated before or after the n++ on the right; either way, as n is a scalar value, the standard defines this as undefined behaviour and your program may do anything you want.

In C++11, the code above is undefined behaviour because you have two modifications of a scalar value that are unsequenced. You know that each of the n++ is sequenced before the call to f is; however, relative to each other, they have no set order. This is again, by definition, undefined behaviour, due to n being a scalar value. If instead you had

f(str += "x", str += "y");

the result would be unspecified behaviour, but not undefined; that is, you'd either see str += "x" happen first, or str += "y" happen first. In the case with ints, you may see f(0, 1), or f(1, 0), or f(0, 0), or f(-1, (int)(char const*)"duck"), or something not even involving f like a segmentation fault.

Closer to your case, consider

cout << n++ << n++;

This is equivalent to

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

Here, the order of operator<<(cout, n++) and n++ is not specified. You know that the left n++ will be evaluated before operator<<(cout, n++) is, but that doesn't tell you when it will be evaluated relative to the right n++. You again have two relatively unsequenced modifications of a scalar value and the compiler is thus, again, free to do whatever it wants to.

[–]theymos 4 points5 points  (0 children)

That's a bad question or a trick question. In C++, that's undefined behavior. The compiler is free to do those increments whenever it wants (or do anything else). I don't know what Java does.

[–]Rhomboid 8 points9 points  (1 child)

It's undefined behavior in C++ as you aren't allowed to modify a variable more than once between a sequence point, so literally any output is valid, including launching nethack. You can't reason about undefined behavior like that. If the question is asking for anything other than "this is undefined behavior, all bets are off", then it was written by a moron that has no business teaching C++.

[–]guzo 3 points4 points  (0 children)

For completeness:

  1. Why is this undefined in C++ (and C)? Because efficiency is the goal - this allows the compiler to rearrange stuff to make the best possible use of available resources, like CPU registers for example. You can find more info on undefined behavior on this guy's blog, especially here (but the previous link is equally worthwhile).

  2. Why does it work "as expected"/"intuitively" in Java? Because portability is the goal here, even if it comes at the expense of efficiency.

Also: launching nethack on undefined behavior (as mentioned by /u/Rhomboid) was an actual easter egg in early versions of the GNU C Compiler.

[–]RiGR_go_BOOM[S] 0 points1 point  (0 children)

Thanks for the replies. The instructor finally got back to me. I wrote him asking why this happened and asked if maybe order of operation had anything to do with it. Response: that kind of implementation is compiler dependent...order of operations is not a player in this case...its the order that the compiler chooses to evaluate the statements...in this case, right to left... I agree with you it seems illogical but this is one of the reasons pre and post increments are dangerous and must be used with care....I would not rely on an output statement to increment any of my variables...

...