18

Can someone explain to me why this code prints 14? I was just asked by another student and couldn't figure it out.

int i = 5;
i = ++i + ++i;
cout<<i;
Cœur
  • 37,241
  • 25
  • 195
  • 267
Alexander Stolz
  • 7,454
  • 12
  • 57
  • 64
  • What compiler/platform are you using? – RobS Dec 04 '08 at 11:43
  • 1
    Just reminded me of why I go Yikes when i see cpp. – Gishu Dec 04 '08 at 11:52
  • 1
    Modifying a value twice in the same expression (in this case your two ++i's) is undefined in both C and C++ and you CANNOT rely on its behavior to be the same across compilers. –  Dec 04 '08 at 13:31
  • 14
    The actual mystery is why you would write such code. – Mark Ransom Dec 04 '08 at 17:01
  • Think deeper and you will see why it is right. It has nothing to do with an undefined behavior. – Null303 Dec 04 '08 at 17:21
  • Null303: Try running it on a couple of other compilers then. It has everything to do with undefined behavior, and there is nothing "right" about it. – jalf Dec 04 '08 at 17:53
  • @Mark Ransom: usually by accident due to use of macros, I'd have thought. Yet another reason etc. – Steve Jessop Dec 04 '08 at 20:44

8 Answers8

44

The order of side effects is undefined in C++. Additionally, modifying a variable twice in a single expression has no defined behavior (See the C++ standard, §5.0.4, physical page 87 / logical page 73).

Solution: Don't use side effects in complex expression, don't use more than one in simple ones. And it does not hurt to enable all the warnings the compiler can give you: Adding -Wall(gcc) or /Wall /W4(Visual C++) to the command line yields a fitting warning:

test-so-side-effects.c: In function 'main':
test-so-side-effects.c:5: warning: operation on 'i' may be undefined
test-so-side-effects.c:5: warning: operation on 'i' may be undefined

Obviously, the code compiles to:

i = i + 1;
i = i + 1;
i = i + i;
phihag
  • 278,196
  • 72
  • 453
  • 469
  • The phrase "The requirements of this paragraph shall be met for each allowable ordering of the subexpressions of a full expression; otherwise the behavior is undefined." leads me to believe the statement is legal, just its behavior is undefined. If by invalid, you meant illegal, I disagree. – paxdiablo Dec 04 '08 at 12:51
  • I don't disagree with your answer (I +1'ed it) but standards are finicky documents and the distiction between invalid, implementation-defined and undefined is an important one. – paxdiablo Dec 04 '08 at 12:52
  • @phihag: +1 if mention sequence points and clarify that the danger is with multipside effects to the same variable. It's not clear in your answer and we're talking to students. – Binary Worrier Dec 04 '08 at 17:04
  • @Pax: normally when people talk about "invalid" code, they mean code yielding undefined behavior. At least, that is my experience. e.g "it is invalid to deref a NULL pointer". Since the standard doesn't define "invalid", we are free to use it to mean something other than illegal or ill-formed. – Steve Jessop Dec 04 '08 at 20:38
  • 1
    Considering how easy C++ makes it to write code that yields undefined behaviour, I think that's a little harsh. I mean, if C++ compilers were as unhelpful as they're allowed to be, I'd set my computer on fire several times a week at the very least... – Steve Jessop Dec 06 '08 at 02:08
  • Can this answer please get a new C++ standard reference? C++11 N1905 has become C++11 N3242 where the examples from N1905 have disappeared. – Thomas Weller May 17 '23 at 13:16
16

That's undefined behaviour, the result will vary depending on the compiler you use. See, for example, C++ FAQ Lite.

Claymore
  • 319
  • 1
  • 5
12

In some of the answers/comments there has been a discussion about the meaning of 'undefined behavior' and whether that makes the program invalid. So I'm posting this rather long answer detailing exactly what the standard says with some notes. I hope it's not too boring...

The quoted bits of the standard come from the current C++ standard (ISO/IEC 14882:2003). There's similar wording in the C standard.

According to the C++ standard, modifying a value more than once within a set of sequence points results in undefined behavior (section 5 Paragraph 4):

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.53) 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. Furthermore, 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 expression; otherwise the behavior is undefined. [Example:

i = v[i++]; // the behavior is unspecified
i = 7, i++, i++; // i becomes 9
i = ++i + 1; // the behavior is unspecified
i = i + 1; // the value of i is incremented

—end example]

Note that the second example, "i = 7, i++, i++;" is defined since the comma operator is a sequence point.

Here's what the C++ standard says 'undefined behavior' means:

1.3.12 undefined behavior [defns.undefined]

behavior, such as might arise upon use of an erroneous program construct or erroneous data, for which this International Standard imposes no requirements. Undefined behavior may also be expected when this International Standard omits the description of any explicit definition of behavior. [Note: permissible undefined behavior ranges from ignoring the situation completely with unpredictable results, to behaving during translation or program execution in a documented manner characteristic of the environment (with or without the issuance of a diagnostic message), to terminating a translation or execution (with the issuance of a diagnostic message). Many erroneous program constructs do not engender undefined behavior; they are required to be diagnosed. ]

In other words, the compiler is free to do whatever it wants, including

  1. spitting out an error message,
  2. doing something implementation defined & documented,
  3. having completely unpredictable results

The second item covers language extensions which most compilers have, but of course are not defined in the standard.

So I guess that strictly speaking something that exhibits undefined behavior is not 'illegal', but in my experience whenever there's been something in a C/C++ program that exhibits 'undefined behavior' (unless it's an extension) - it's a bug. I think that calling such a construct illegal is not confusing, misleading, or misguided.

Also, I think trying to explain what the compiler is doing to reach the value 14 is not particularly helpful, as that misses the point. The compiler could be doing almost anything; in fact, it's likely that the compiler may reach a different result when it's run using differing optimization options (or may produce code that crashes - who knows?).

For those who want some additional references or an appeal to authority, here are some pointers:

Steve Summit's (maintainer of the comp.lang.c Frequently Asked Questions) long, long answer on this topic from 1995:

Here's what Bjarne Stroustrup has to say on the matter:


Footnote: the C++ standard uses the word 'illegal' exactly once - when describing a difference between C++ and Standard C regarding the use of static or extern with type declarations.

Michael Burr
  • 333,147
  • 50
  • 533
  • 760
  • It is undefined behavior, but in this case the expression has only one possible result, which is 14. Do you program by logic or by law? – Null303 Dec 04 '08 at 17:56
  • 2
    Null303, it does not in C++. You may think you understand expression evaluation, but the standard says differently. Guess who compiler makers are listening to? – peterchen Dec 04 '08 at 18:39
  • 1
    "only one possible result". Even aside from the fact that it's undefined, I don't think 13 would be a completely indefensible result of the expression given. Increment and evaluate once, increment and evaluate again, add the two. 13. I'd like to see the "logic" says it must be 14 an all compilers. – Steve Jessop Dec 04 '08 at 20:43
  • @Null303 - Digital Mars returns 13. You'll need to take this up with Walter Bright who writes that compiler. But I don't think you'll get very far. – Michael Burr Dec 04 '08 at 20:48
  • Can this answer please get a new C++ standard reference? C++11 N1905 has become C++11 N3242 where the examples from N1905 have disappeared. – Thomas Weller May 17 '23 at 13:17
4

Simple... you compiler is evaluating BOTH increments before performing the sum, without caching the intermediate results. This means that when you add i twice, it now has the value of 7.

If you do

int j=++i; 
int k=++i;

i = j+k;

you'll see 13 as expected.

bgoncalves
  • 1,687
  • 3
  • 19
  • 19
  • I don't really think 13 is expected. I would expect an exception being thrown, but I guess that's just me... – Paulius Dec 04 '08 at 12:30
  • 1
    An exception for what exactly? You haven't stated why this code is illegal according to the standard. – paxdiablo Dec 04 '08 at 12:37
1

I think that when looking at the problem from the sight of the syntax tree, the answer to the problem becomes clearer:

i
|
=
|
+
|
unary expression - unary expression

unary expression: unary operator expression

In our case expression boils down to the variable i.

Now what happens is that both unary expression modify the same operand, so the code does two times ++i when evaluating the unary expressions before adding the results of both unary expressions.

So what the code does is indeed

++i;
++i;
i = i + i;

For i = 5 that means

i = i + 1; //i <- 6
i = i + 1; //i <- 7
i = i + i; //i <- 14

Razzupaltuff
  • 2,250
  • 2
  • 21
  • 37
0

A better question would be, is it always going to be 14?

int i = 5;
i = ++i + ++i;
cout<<i;

i = ++i   + ++i   ;
i = ++(5) + ++(5) ;
i =    6  +    6  ;
i = 12;

i = ++i   + ++i   ;
i = ++i   + ++(5) ;
i = ++i   +   (6) ;
i = ++(6) +    6  ;
i =   (7) +    6  ;
i = 13;

i = ++i   + ++i   ;
i = ++i   + ++(5) ;
i = ++(6) +   (6) ;
i =   (7) +   (7) ;
i = 14;

In all likelihood it will probably be 14, because it makes slightly more sense.

Brad Gilbert
  • 33,846
  • 11
  • 78
  • 129
-2

Because the prefix increment has precedence:

int i = 5;
i = i+1; // First ++i, i is now 6
i = i+1; // Second ++i, i is now 7
i = i + i // i = 7 + 7
cout << i // i = 14
Gonzalo Quero
  • 3,303
  • 18
  • 23
  • By precedence rules it would be 13. i++ returns the value after the update. So it should be i = i + 1 /*6*/, i = i + 1 /*7*/, i = 6 + 7. It's actually undefined behavior due to the sequence point rule. – MSalters Dec 04 '08 at 12:37
  • Assuming you meant "++i", @MS. – paxdiablo Dec 04 '08 at 12:39
  • Where did I talk about "i++"? Maybe is my English, but, isn't "++i" prefix and "i++" suffix? – Gonzalo Quero Dec 04 '08 at 12:55
  • @Gonzalo, the @MS in my comment means it was directed at MSalters' comment (the first one), not your answer. – paxdiablo Dec 04 '08 at 13:02
-2
 i = i++ + i; //11  

 i = i++ + i++; //12

 i = i++ + ++i; //13

 i = ++i + i++; //13

 i = ++i + ++i; //14    
plan9assembler
  • 2,862
  • 1
  • 24
  • 13