14

I have the following code.

    int x=80;
    int &y=x;
    x++;
    cout<<x<<" "<<--y;

The output comes out to be 80 80. And I don't understand how. I thought the output of x would be 81 although I don't know anything about y. How is the reference variable affected by the decrement operator. Can someone please explain?

Mat
  • 202,337
  • 40
  • 393
  • 406
Emperor Penguin
  • 394
  • 2
  • 8
  • 1
    Probably this question should help clarify it for you: What are the differences between pointer variable and reference variable in C++? (http://stackoverflow.com/questions/57483/what-are-the-differences-between-pointer-variable-and-reference-variable-in-c) – yasouser Aug 25 '13 at 07:31
  • This is similar (to http://stackoverflow.com/questions/18426473/pre-increment-not-working-as-i-expect/18426505#18426505). You are missing a sequence point. –  Aug 25 '13 at 07:36

4 Answers4

19

The expression is evaluated as:

((cout << x) << " ") << --y;

There is no sequence point (or ordering) between the evaluation of the left-hand side and the right-hand side of the expression, the compiler can output code that evaluates --y as the first step.

Since y is a reference to x here, this is actually undefined behavior since you're both reading from and writing to x in the same expression without intervening sequence points.

Mat
  • 202,337
  • 40
  • 393
  • 406
  • Please check the edited code This surely has some thing to do with the pointer arithmetic. When we say y has an address x i.e, 80 and try and decrement y it is not decrementing on my system too. – Sanyam Goel Aug 25 '13 at 07:41
  • 4
    @SanyamGoel: it's undefined behavior. Different compilers can (and will) output different things. The order of evaluation of the LHS and RHS is implementation-defined, plus a read and a write to `x` without sequence points => undefined behavior (i.e. it's not proper C++, it can do anything). – Mat Aug 25 '13 at 07:43
  • Suggested edit: *"See [this SO thread](http://stackoverflow.com/questions/4176328/undefined-behavior-and-sequence-points) for a detailed explanation."* – jrok Aug 25 '13 at 07:46
  • @SanyamGoel: It has everything to do with sequence points and nothing with pointer arithmetic. Check out [http://ideone.com/r6DMPV] and [http://ideone.com/iOJmZS] – Binayaka Chakraborty Aug 25 '13 at 07:47
  • @jrok: indeed. I'd actually be voting to close as dup (there's a great match pointed to by Alok) if the damn close thing was working... – Mat Aug 25 '13 at 07:47
  • Why are duplicates mass upvoted? –  Aug 25 '13 at 07:47
  • if we write cout< – Freedom911 Aug 25 '13 at 07:48
  • @Mat heh, somebody downvoted you for that :) – jrok Aug 25 '13 at 07:49
  • @remyabel: I don't know. I plead guilty to this one but hadn't found the appropriate dup after a few minutes. As said earlier, will VTC when that works (close voting is currently non-functional). – Mat Aug 25 '13 at 07:49
  • @Mat: Yes, the close voting is not working. And I could find it only because I remembered answering on it not so long ago otherwise it's not so obvious to find it, so there is no reason to feel guilty about it. – Alok Save Aug 25 '13 at 07:50
6

One nasty thing about redirection operators << is that they intuitively convey an indea of "sequential computation" that indeed is not present.

When you write

std::cout << f() << g() << std::endl;

the output will show first the result of f() and then the result of g(), but the actual call to g() may happen before the call to f().

It even gets worse than this... it's not that the sequence is not predictable, but that indeed the very concept of sequence is not valid. In

std::cout << f(g()) << h(i()) << std::endl;

it's for example legal that the first function being called is g(), followed by i(), followed by h() and finally by f(). It's not even guaranteed that order will be the same for all invocations (not because compiler makers likes to make fun of you, but because the code can be inlined and the compiler may decide a different order if the containing function is inlined in a different context).

The only C++ operators that guarantee a sequence in the evaluation order are:

  1. &&: first evaluates left side and only if the result is "true" evaluates the right side
  2. ||: first evaluates left side and only if the result is "false" evaluates the right side
  3. ?:: evaluates first the condition and then only the second or the third operand
  4. ,: the comma operator... evaluates the left side, drops the value and then evaluates and returns the right side. NOTE: the commas between function parameters are NOT comma operators and no evaluation order is imposed.

Moreover this guaratee is valid only for the predefined operators. If you overload &&, || or , in your class they're just normal operators without any special restrictions on evaluation order.

Any other operator doesn't impose any restriction on the evaluation order, and this includes << even if the usage sort of tricks you into thinking that.

6502
  • 112,025
  • 15
  • 165
  • 265
1

This is undefined behavior , C/C++ standards don't define the way argument are pushed into the stack, and usually argument are pushed in the reverse order, for example here:

func(1, 2)

will be evaluated to something like:

push 2
push 1
call func

so in your case, --y is evaluated and pushed before x does. It is clear in this example:

#include <iostream>

int a() { std::cout << "a" << std::endl ; return 1; }
int b() { std::cout << "b" << std::endl ; return 2; }

int main(void) {

    std::cout << a() << " " << b() << std::endl;

    return 0;
}

from the first look, it should print:

a
b
1 2

but it prints:

b
a
1 2
0
x++

increments x and produces as expression result the original value of x.

In particular, for x++ there is no time ordering implied for the increment and production of original value of x. The compiler is free to emit machine code that produces the original value of x, e.g. it might be present in some register, and that delays the increment until the end of the expression (next sequence point). Althought x++ seems to increment x to 81, it is not doing it until the print. As per the --y, it is getting the incremented value(81) and decrementing it before printing it as it is a prefix operator.

Andromeda
  • 1,370
  • 2
  • 10
  • 15