Int a = 5; a = a++ + ++a; a =? (2011)

Hacker News Top News

Summary

Analyzes the undefined behavior of the C/C++ expression 'a = a++ + ++a;' for int a=5, demonstrating three possible results (11, 12, 13) due to compiler-dependent evaluation order and post-increment handling, with theoretical and experimental breakdown.

No content available
Original Article
View Cached Full Text

Cached at: 05/14/26, 06:25 PM

# int a = 5; a = a++ + ++a; a = ? Source: [https://gynvael.coldwind.pl/?id=372](https://gynvael.coldwind.pl/?id=372) I've received the title riddle from furio and I found it interesting enough to pass it during the next few days to everyone that might be even remotely interested in C/C\+\+ problems\. The interesting thing here is the Undefined Behavior \(UB\), well\.\.\. actually two UBs, thanks to which there are three possible correct answers: 11, 12 and 13\. Let's start with theoretical considerations on the possible answers and then get to empirical results \(initially gathered by nism0, but later extended by readers from[the Polish side of the mirror](https://gynvael.coldwind.pl/?id=369)\)\. OK\.\.\. so, which places are UB/unknown/compiler\-dependent? **First UB** First, we don't know which**a**will get fetched first \(by*fetching*I mean copying the value from memory to some internal register\)\. There are two possibilities here: Possibility 1\. The first**a**will get fetched first, then the pre\-increment will take place, and then the second**a**will be fetched \(I'll skip the post\-incrementation problem for now\): `a = a \+ \+\+a;` `Step 1\. Fetch first a\.a = 5 \+ \+\+a; \(a==5\)` `Step 2\. Pre\-increment a\.a = 5 \+ a; \(a==6\)` `Step 3\. Fetch second a\.a = 5 \+ 6; \(a==6\)` `Step 4\. Calculate the addition\.a = 11;` Possibility 2\. The pre\-increment will take place first and the result will be stored in memory, and then the fetching of**a**will take place\. `a = a \+ \+\+a;Step 1\. Pre\-increment a\.a = a \+ a; \(a==6\)Step 2 i 3\. Fetching both a\.a = 6 \+ 6; \(a==6\)Step 4\. Calculate the addition\.a = 12;` So, even with skipping the post\-incrementation we get two different possibilities \(11 and 12\)\.**Second UB** The second UB is related to the post\-incrementation and the potentially trivial line**a = a\+\+**\. As it occurs, there are also two possibilities here \(I'll demonstrate them using**int a = 5; a = a\+\+;**as an example\)\. Terminology: **a\_mem**\-**a**still in memory \(e\.g\. as a local variable somewhere on the stack\) **a\_copy**\- a previously fetched copy of**a**in some internal register Possibility 1\. Post\-increment goes Missing In Action: `Initial setup: \(a\_mem == 5, a\_copy == n/a\)` `Step 1\. Fetching a\.a = 5\+\+; \(a\_mem == 5, a\_copy == 5\)` `Step 2\. Post\-increment on the variable \(in memory\)\.a = 5; \(a\_mem == 6, a\_copy == 5\)` `Step 3\. "Set" gets executed \- a\_copy is moved to a\_mem\.\(a\_mem == 5, a\_copy == n/a\)` In the above case the result of post\-incrementation went MIA \- it got written to the memory but it was soon overwritten by another \(not incremented\) value\. \(Actually I've always thought that post\-increment is always done after all operations finish so personally I would treat this behavior as a compiler bug\.\)Possibility 2\. Post\-incrementation is deferred to the very end\. `Initial setup: \(a\_mem == 5, a\_copy == n/a\)` `Step 1\. Fetching a\.a = 5\+\+; \(a\_mem == 5, a\_copy == 5\)` `Step 2\. "Set" gets executed \- a\_copy is moved to a\_mem\.\(a\_mem == 5, a\_copy == n/a\)` `Step 3\. Post\-increment on the variable \(in memory\)\.a\+\+ \(a\_mem == 6, a\_copy == n/a\)` So, the post\-increment is executed at the very end of the calculations and it does not get overwritten\.**Summary of UBs** To sum up the two previously described UBs for the**a = a\+\+ \+ \+\+a**equation we get: Possibilities 1 i 1: 5\+6 and the post\-increment goes MIA,**result: 11** Possibilities 2 i 1: 6\+6 and the post\-increment goes MIA,**result: 12** Possibilities 1 i 2: 5\+6 and the post\-increment is deferred,**result: 12** Possibilities 2 i 2: 6\+6 and the post\-increment is deferred,**result: 13** **Experimental results** Now for the empirical results \(thx to nism0 for the initial tests and the initial table, and to nonek and qyon for spotting certain typos\): Code 1Code 2Code 3Code 4Code 5Code 6int a = 5; a = a\+\+ \+ a\+\+;int a = 5; a = a\+\+ \+ \+\+aint a = 5; a = \+\+a \+ a\+\+;int a = 5; a = \+\+a \+ \+\+a;int a = 5; a = a\+\+;int a = 5; a = a \+ \+\+a; Compiler/LangVer\.Result 1Result 2Result 3Result 4Result 5Result 6gcc2\.9512131413612gcc4\.112131413612gcc4\.212131413612gcc4\.2\.1 Apple12131413612gcc4\.312131413612gcc4\.3\.312131314612gcc4\.4\.412131314612gcc4\.6\.0 \(exp\.\)12131413612gcc4\.5\.1 MinGW6412131314612tcc0\.9\.25????????512bcc0\.16\.17????????512Microsoft C/C\+\+16\.00\.30319\.01 \(80x86\)12131314612Embarcadero C\+\+6\.31 for Win3212131314612Intel C\+\+12\.0\.1\.12712131313612Keil C9\.0211121213612SDCC3\.0\.1 \#609211121314512clang2\.811121213511clang1\.6 Apple11121213511PHP5\.2\.1011121213512java1\.6\.0\_0611121213511javac1\.4\.2\_1211121213511java1\.6\.0\_2111121213511javac1\.6\.0\_2211121213511C\#2\.011121213511C\#4\.011121213511C\#Mono 2\.6\.411121213511Borland Turbo C\+\+ for DOS2\.0112131314612HiSoft C for ZX Spectrum1\.311121213512Thanks for additional results to:[Icewall](http://icewall.pl/),[Krzysztof Kotowicz](http://blog.kotowicz.net/)\(PHP 5\.2\.10\), mlen \(2x clang, 2x gcc\), none'a \(2x Java\),[Keraj](http://keraj.net/)\(2x Java\), MDobak \(SDCC & Keil C\), garbaty lamer \(3x C\#, Turbo C\+\+, HiSoft C\), Xgrzyb90 \(gcc 4\.4\.4\), no\_name \(gcc 4\.3\.3\), dikamilo \(mingw64 4\.5\.1\) Garbaty lamer has also posted \(in the comments on the Polish side of the mirror\) a really cool screenshot from the**HiSoft C for ZX spectrum**\(click to zoom\): [![HiSoft C for ZX Spectrum, screen by garbaty lamer](https://gynvael.coldwind.pl/img/t_hisoftgynvael_garbaty_lamer.png)](https://gynvael.coldwind.pl/img/hisoftgynvael_garbaty_lamer.png) If you would like to test your compiler \(posting back the results in the comments is really appreciated, especially from strange/uncommon compilers and other languages which support pre\- / post\- increments ;\>\) you can use the code \(made by nism0\) at the bottom of the post \(see Appendix 3\)\. And that's that ;\> **Appendix 1:** In the comments on[the Polish side of the mirror](https://gynvael.coldwind.pl/?id=369)Rolek has suggested to apply the same test to overloaded operators \(see the comments on the Polish side for the code\)\. Results \(by Rolek from MSVC\+\+ and me from g\+\+\): Code 1Code 2Code 3Code 4Code 5Code 6int a = 5; a = a\+\+ \+ a\+\+;int a = 5; a = a\+\+ \+ \+\+aint a = 5; a = \+\+a \+ a\+\+;int a = 5; a = \+\+a \+ \+\+a;int a = 5; a = a\+\+;int a = 5; a = a \+ \+\+a; Compiler/Lang\.Ver\.Result 1Result 2Result 3Result 4Result 5Result 6Microsoft C/C/\+\+ \(w/o overloading\)16\.00\.30319\.01**12**13**13**14**6**12Microsoft C/C/\+\+ \(with overloading\)16\.00\.30319\.01**11**13**12**14**5**12g\+\+ \(w/o overloading\)4\.5\.0 MinGW**12**13**13**14**6**12g\+\+ \(with overloading\)4\.5\.0 MinGW**11**13**12**14**5**12Also, krlm has posted a link to a good article about[sequence points](http://en.wikipedia.org/wiki/Sequence_point)\. **By the way\.\.\.** If want to improve your binary file and protocol skills, check out the workshop I'll be running between April and June →[Mastering Binary Files and Protocols: The Complete Journey](https://hackarcana.com/workshop-session/2025-Q1-Q1-mastering-binary/buy?utm=gyn-blog-inad) **Appendix 2:** Garbaty lamer \(again, on[the Polish side of the mirror](https://gynvael.coldwind.pl/?id=369)\) has mentioned that in C\# this riddle isn't actually a riddle \- it's well defined behavior, see C\# specification point §7\.3: *Operands in an expression are evaluated from left to right\. For example, in F\(i\) \+ G\(i\+\+\) \* H\(i\), method F is called using the old value of i, then method G is called with the old value of i, and, finally, method H is called with the new value of i\. This is separate from and unrelated to operator precedence\.* **Appendix 3:** Code for testing: `\#include <stdio\.h\>` `int main\(void\)\{ int a = 5, b = 5, c = 5, d = 5, e = 5, f = 5;` `// test 1 a = a\+\+ \+ a\+\+; printf\("%i \\n",a\);` `// test 2 b = b\+\+ \+ \+\+b; printf\("%i \\n",b\);` `// test 3 c = \+\+c \+ c\+\+; printf\("%i \\n",c\);` `// test 4 d = \+\+d \+ \+\+d; printf\("%i \\n",d\);` `// test 5 e = e\+\+; printf\("%i \\n",e\);` `// test 6 f = f \+ \+\+f; printf\("%i \\n",f\);` `// done return 0;\}` **Appendix 4:** A comment from Cem Paya: \-\-start\-\- Similar to C\#, this is also not a riddle for Java because Java defines evaluation to be strictly left\-to\-right\. See section 15\.7 here for some examples with side\-effects as in your case: [http://java\.sun\.com/docs/books/jls/second\_edition/html/expressions\.doc\.html](http://java.sun.com/docs/books/jls/second_edition/html/expressions.doc.html) In C\+\+ where it is undefined, the result can also depend on the optimization level used during compilation, which can change the number of times a value is referenced\. eg the compiler expects "a" to not change its value during the evaluation and may optimize other occurences to the same one it fetched\. ==end==

Similar Articles

When compilers surprise you

Lobsters Hottest

Matt Godbolt explores compiler optimizations that convert an O(n) summation loop into an O(1) closed-form solution, highlighting how Clang and GCC employ sophisticated techniques like loop unrolling and mathematical simplification to dramatically improve code performance.

C++26: Standard library hardening

Lobsters Hottest

C++26 is introducing standardized library hardening to catch common undefined behavior (like out-of-bounds access) at runtime, based on Google's production experience showing a mere 0.30% performance overhead and a 30% reduction in segmentation faults.