2

Please be easy on me and don't shoot me as I'm still newbie.

I'm totally confused and can't for life figure out why when I run this code:

int y = 9;
cout << "++y = " << ++y << "\n--y = " << --y << "\ny++ = " << y++ << "\ny-- = " << y-- << "\n";
cout << "y = " << y << "\n";

I get the following results:

y = 9
++y = 9
--y = 9
y++ = 8
y-- = 9
y = 9

instead of these results:

y = 9
++y = 10
--y = 9
y++ = 9
y-- = 10
y = 9

That I get from this code:

int y = 9;
cout << "y = " << y << "\n";
cout << "++y = " << ++y << "\n";
cout << "--y = " << --y << "\n";
cout << "y++ = " << y++ << "\n";
cout << "y-- = " << y-- << "\n";
cout << "y = " << y << "\n";

Can anyone explain -in simple words as possible- what happens in the first code so that it prints the result that way?

XO39
  • 481
  • 2
  • 9
  • 22
  • 1
    As you can see, it is so interesting and complicated question, that even non-newbies argues on it. Thank you for good question. +1. – Gangnus Jan 19 '12 at 20:47
  • Indeed! I just hoped there was a clear and logic reason to explain it as I'm pretty sure we're going to get a similar question in the final exam and don't want to lose any marks because of it. :( – XO39 Jan 19 '12 at 20:52
  • possible duplicate of [Could anyone explain these undefined behaviors (i = i++ + ++i , i = i++, etc...)](http://stackoverflow.com/questions/949433/could-anyone-explain-these-undefined-behaviors-i-i-i-i-i-etc) – Bo Persson Jan 19 '12 at 21:13

3 Answers3

11

A simple rule is that you are not expected to increment the same location more than once in any given statement. So you should not code cout << y++ << ++y << endl; which contain two increments of y (assuming an int y; declaration).

For details, read about sequence points and undefined behavior in the C++ standard.

There are lot of related questions. Look into them for more!

Basile Starynkevitch
  • 223,805
  • 18
  • 296
  • 547
  • Thanks, Basile. We were asked to give the output of the first code by our teacher in an exam. My answer was the same as the one from the second code. When I got home I tried it and found out I was totally wrong, so I wanted to understand the logic behind that. *(Added int declaration)* – XO39 Jan 19 '12 at 20:12
  • No. It is **error**. You can not predict the order of evaluation of ++ and --. But the result of a=1; b=a++ + a++; is absolutely (ANSII) predictable. It is 5. – Gangnus Jan 19 '12 at 20:15
  • 1
    Again, I see more than ten related questions on the right part of the browser. Look into them! – Basile Starynkevitch Jan 19 '12 at 20:17
  • @Gangnus: no, it's not. If well-defined, it would be 2 or 3, not 5. But it's undefined behaviour, so it could also be 42. – R. Martinho Fernandes Jan 19 '12 at 20:18
  • It is worse than unpredicatable: it is undefined behavior, and as I joke sometimes, the computer could explode and kill you and still have a standard conforming implementation. You just should *never* code that. Use the `-Wall` option of the `gcc` compiler to get more warnings... Understand that having code with undefined behavior is a shame (it is non professional and you should avoid coding that). – Basile Starynkevitch Jan 19 '12 at 20:18
  • No, it is not the undefined behaviour, according to ANSII. If you think that it is, please put here the appropriate paragraph in the ANSII standard of C++. Undefined is **the order of evaluation** only. At least it is what I told to my students. --- BTW, using a personal position in discussion merely means that you don't have normal arguments. – Gangnus Jan 19 '12 at 20:23
  • @BasileStarynkevitch: I already searched for an explanation for this before posting and couldn't find an answer, sorry if it was duplicated. – XO39 Jan 19 '12 at 20:25
  • By saying it's undefined or unpredictable behavior, do you mean there is no clear explanation for this?! – XO39 Jan 19 '12 at 20:26
  • @Gangnus: The order of evaluation is *unspecified*. Reading and writing from the same object without an intervening sequence point is undefined behaviour (see more here: http://stackoverflow.com/questions/4176328/undefined-behavior-and-sequence-points). – R. Martinho Fernandes Jan 19 '12 at 20:32
  • It practically means the compiler can do anything it wants, and should not even bother to detect the anomaly. So in practice, different compilers may give different behavior, and you should not trust what your particular compiler does! – Basile Starynkevitch Jan 19 '12 at 20:32
  • No, undefined is an official termin, that means, that it is not defined by standard ANSII. So, the behaviour is absolutely unpredictable. Sometimes, it is set there, that it should be defined for a compiler. Then it is predictable, but only for a concrete compiler. For another compiler the behaviour could be different, but again, predictable for that compiler – Gangnus Jan 19 '12 at 20:34
  • I am not sure that compiler should have predictable behavior. And some versions of GCC on some systems are actually using random number generators for weird things. So with such a compiler the behavior might not be reproducible from one run to the next! – Basile Starynkevitch Jan 19 '12 at 20:35
  • please, look at 4th paragraph here. http://www.csci.csusb.edu/dick/c++std/cd2/expr.html There you can find the exact text. – Gangnus Jan 19 '12 at 20:41
  • @BasileStarynkevitch. Speaking on compiler specific behaviour, I don't mean this case. Only trying to explain terms. – Gangnus Jan 19 '12 at 20:42
  • But the question is about C++, in a context where `<<` is a call to some stream operation... – Basile Starynkevitch Jan 19 '12 at 20:45
  • @Gangnus: The part of the standard you referenced has the quote stating that this is UB: "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 ... otherwise the behavior is undefined." – Benjamin Lindley Jan 19 '12 at 21:00
  • My excuses, my error, slip of my memory. @BasileStarynkevitch please, edit some symbol in your answer for I want to get my downvoting back. Shame on me. – Gangnus Jan 19 '12 at 21:20
  • @Gangnus: I put *C++* in bold. – Basile Starynkevitch Jan 20 '12 at 05:55
  • @BasileStarynkevitch done. And again I am bringing my excuses. – Gangnus Jan 20 '12 at 07:55
3

When according to the rules operation * is to be counted before +, and ++ before *, it will be so.

 a*b++ + c // first b++ (returns **old** b), than a*b, than ...+c

But when you have a++ * a--, nobody can tell, what of the two operands, a++ or a-- will be evaluated the first. According to ANSII standard, even if you use the same translator, the result is every time unpredictable.

cite from the C++ ANSII standard:

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. Between the previ- ous and next sequence point a scalar object shall have its stored value modified at most once by the evaluation of an expression. Fur- thermore, the prior value shall be accessed only to determine the value to be stored. The requirements of this paragraph shall be met for each allowable ordering of the subexpressions of a full expres- sion; otherwise the behavior is undefined. [Example:

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

      i = ++i + 1;     // the behavior is undefined 
      i = i + 1;       // the value of 'i' is incremented

Sequence points:

  • at the end of the evaluation of a full expression (a full expression is an expression statement, or any other expression which is not a subexpression within any larger expression);
  • at the ||, &&, ?:, and comma operators;
  • and at a function call (after the evaluation of all the arguments, and just before the actual call).

So, || is a sequence point, but << is not.

Gangnus
  • 24,044
  • 16
  • 90
  • 149
  • I don't think that parenthesis are enough to make `a++ * a--` legal. It is **undefined behavior** even if coded `(a++) * a--` etc.... You need to make one of the increment/decrement in a separate *statement*. – Basile Starynkevitch Jan 19 '12 at 20:16
  • Yes, you are right here. They are not enough. Even more - it is impossible to make the compilator to give priority to one or the other ++ or -- – Gangnus Jan 19 '12 at 20:25
  • yes, the words on "the same level" was chosen badly. They are senseless here. Sorry. Removed. – Gangnus Jan 19 '12 at 20:28
2

Mulitiline version of first code should be:

  y = 9;
  cout << "y-- = " << y-- << "\n";
  cout << "y++ = " << y++ << "\n"
  cout << "--y = " << --y << "\n"
  cout << "++y = " << ++y << "\n"
  cout << "y = " << y << "\n";
ldanko
  • 557
  • 8
  • 20
  • Yes, except perhaps `endl` instead of `"\n"` – Basile Starynkevitch Jan 19 '12 at 20:21
  • Yes, I know that. I just was wondering how it assigns the value of the code in the first code and why it prints it that way while the final value of *y* in both codes is the same! – XO39 Jan 19 '12 at 20:21
  • 1
    Value is the same in both code pieces, because in first and second code you have the same operations, and addition is cumulative. – ldanko Jan 19 '12 at 20:25
  • @BasileStarynkevitch: Is there any real benefits of using `endl` over `"\n"`? – XO39 Jan 19 '12 at 20:29
  • 1
    Yes, `endl` may flush the buffer on occasions. I'm not sure `"\n"` should always flush (in practice it often does, because of compatibility of C++ streams with C FILE-s). I'm not sure that `"\n"`would flush a `stringstream` – Basile Starynkevitch Jan 19 '12 at 20:30