This would be my very first technical post. I would like to give special thanks to my buddy, Joshua Na, for exploring this topic with me in details over Skype on a Friday afternoon! It seems that I didn’t get this right all along throughout my ten years of programming, not until now! I would also like to thank Sergey for pointing out a possible fundamental error in the first cut of my article. For now, I supposed this article is considered complete. Please feel free to discuss in the comments below.
The Empirical Approach
This article started off as an empirical approach, where I try out different codes and attempt to explain the output by spotting the pattern, which explains the title of this article. I’m not sure whether I got this right as I didn’t manage to look at the assembly.
Going beyond, there were many comments that flowed in regarding this and I read up a whole lot more, looking at many conflicting school of thoughts on this. I decided I should just try to understand it my own way based on whatever I have. Does it really matter to know the absolute truth? Or perhaps it’s better to know how to apply. 😛
What are pre-increments and post-increments operators?
In case you are not sure what those stuff are, in codes, they are the ++:
int x = 1;
//Pre increment - ++x
int y1 = ++x;
//Post increment - x++
int y2 = x++;
In a pre-increment (line 4), x will be incremented by one before y1 takes the value of (the new) x.
In a post-increment (line 7), y2 is going to take the value of x, after that, x gets incremented by one.
This looks very straight forward. It seems that in pre-increment, the ++ operator is evaluated first before the statement does while in post-increment, the statement gets evaluated first, before the post-increment operator.
The problem comes, when you try to refer to the Order of Precedence Table like this one:
Does Order of Precedence have anything to do with Order of Evaluation?
In many of the lectures that I sit through in the university, I remembered there seems to be such a table on order of precedence of operators shown to us when we talked about order of evaluation. Hence, if we were to follow the charts like the one above, and this is pretty much the same for Java, C++, C#, the post-increment is ranked higher than the pre-increment. This means that the post-increment is supposed to be evaluated first. To me, this doesn’t make any sense. In fact, I thought that the post-increment should be ranked all the way at the bottom as the increment only takes place after the statement is completed!
If you look through Stack Overflow and programming forums, you might find that quite a number of them will tell you that operator precedence has nothing to do with the order of evaluation. Well, that could be a reason for this strange phenomenon then. But that would have meant that those professors taught us the wrong stuff! What’s going on here?
Anyway, does the order of precedence have anything to do with the order of evaluation?
I would like to refer to some materials from two articles:
Order of precedence for operators defines how expressions are built from their source code representations.
From cppreference.com
Quoting from the example found in cppreference.com, the operator precedence will be able to tell us that f1() + f2() + f3() will be parsed as (f1() + f2()) + f3() due to the left-right associativity of the + operator. Note, this still does not explain which of the functions, f1, or f2 or f3, will get evaluated first.
The above example, or at least that’s how I like to interpret, says that operator precedence has no relationship with the order of evaluation.
The precedence and associativity of C operators affect the grouping and evaluation of operands in expressions. An operator’s precedence is meaningful only if other operators with higher or lower precedence are present. Expressions with higher-precedence operators are evaluated first.
From Precedence and Order of Evaluation, MSDN
The article from MSDN seems to suggest otherwise.
The Experiments
MSDN states that operator precedence is meaningful only if other operators with higher or lower precedence are present. This is perfectly understandable or otherwise, there will be nothing to compare with. Hence, for my experiments, I decided to put pre-increment and post-increment operators in an expression where the variable is used again in a single expression, something like this one:
i = ++i + i++;
I understand that from cppreference.com, said statements like the one above are considered undefined behaviours.
Nevertheless, I did try it, and the experiments produced deterministic results. In the next few paragraphs below, I would like to share my findings with you:
Experiment Group 1
Consider the following codes:
int x = 1; int y = x++ + x;
The value of y is 3. The post-increment does not happen at the end of the statement (or at least that was how I thought I was taught in uni). What is happening here is this:
- x++ gets evaluated.
- The x in x++ (ie. the first x) retains the value of 1.
- The increment will take effect for the second x, making the value of the second x, 2.
- Hence, y = 1 + 2 = 3
The example above shows that x++ does not wait till the evaluation of the statement before the increment takes place. In fact, it probably got evaluated first, which is in coherence to the operator precedence table. All the post-increment operator was saying is that the original x attached to the post-increment operator will not be incremented.
I experimented with expressions that contain both x++ and ++x, and it seems that x++ will get computed first before ++x as per the chart.
Experiment Group 2
Consider the following codes:
int x = 1; int y = x + x++;
Now that we know that the post-increment operator will get evaluated first, I expected that the value of y would be 3, going by the very same steps I listed above.
Actually, the answer is no! The value of this y is 2.
Let’s consider another set of codes:
int x = 1; int y = x + x++ + x;
In the above codes, the value of y is 4. How does that happen? This is my theory:
- x++ gets evaluated.
- The x in x++ (ie. the second x) retains the value of 1.
- The increment will take effect for all x that exist to the right of x++, making the value of the third x, 2. The first x however, because it is on the left side of the equation, will not be incremented.
- Hence, y = 1 + 1 + 2 = 4
I tried out with many more examples, using all four pre and post increments and decrements operators. The results seems consistent with my thinking.
As to exactly why this is so, I’m not sure. It might have been something to do with the memory stack or the way codes are compiled that it seems impossible to apply the increments towards the left. Anyway, this is definitely cleaner if there are a lot of increments and decrements that are found within the same statement.
Results of the Experimentation
I think I will refuse to give an actual answer on whether operator precedence equates to evaluation precedence. I probably can’t answer that based on the conflict material that the Internet had to present.
BIG DISCLAIMER FIRST: The next statement would be a personal opinion on how I understand and how I would like to see this whole situation as. I would like to interpret these findings as a yes, the operator precedence does affect evaluation precedence. Even if not directly, then indirectly somehow.
And based on the results of my experimentations, it also appears that pre-increments and post-increments have very deterministic rules as well, at least on the two systems that I am using:
- Post-increments will be evaluated first. The variable with the post-increment operator will retain its value. The same variable found to the right in the very same expression will be incremented as well.
- Pre-increments will be evaluated next. The variable with the pre-increment operator will be incremented. The same variable found to the right in the very same expression will be incremented as well.
- When multiple post-increments or pre-increments are found in the same equation, evaluation will take place from the left to the right.
- And yes, there is probably nothing wrong with the Order of Precedence table. It just is what it is.
Final Words
I hope you find this article useful. If you have any feedback or any other views on how it should work, please leave me a comment. I would love to hear more from you. 🙂
However, especially for those who are more confused after reading all these, perhaps a better solution is for us to stop writing complicated codes. I’m sure we can achieve the same computation across several statements rather than mixing variables undergoing increments with the same variables in a single expression. Pretty sure it will not make any difference to the performance of the application. Let’s keep it simple shall we?
Precedence has nothing to evaluation – it only specifies the grouping (which operator acts on which subexpressions). Worse than that, with x of type int, “x++ + x”, “x + x++”, and “x + x++ + x” are ERRORS in C++ (and in C, for that matter). It’s just a click away from the page you show: http://en.cppreference.com/w/cpp/language/eval_order
LikeLike
Hey Sergey, thanks for the resources on precedence. I’ll definitely go read it up.
LikeLike
it was useful article
thank you
LikeLike
Thank you so much for your comment yassir 🙂 I’m glad that you found the information here useful.
LikeLike
Very helpful. I had the same question but could not find any reasonable explanation online until I bumped into this page. Thanks.
LikeLike
Hi Eric,
Thank you for your kind words and feedback. 🙂
LikeLike
This article was indeed most helpful. The precedence of the post-increment operator over the pre-increment operator explains it all now.
However, the results of some of your experiments are not in accordance with the ones I obtained while trying out the codes myself. I use Turbo C++, and the value of y in both the cases
int x = 1;
int y = x++ + x;
and
int x = 1;
int y = x + x++;
turned out to be 3, as you had initially expected them to be, instead of the values of 3 and 2 respectively, which you obtained.
Perhaps this is due to the different evaluation procedure followed by different compilers. Turbo C++ does not seem to differentiate between the two based on the order in which x and x++ appear and seems to rely solely on precedence.
Likewise, the value of y that I obtained for the code
int x = 1;
int y = x + x++ + x;
was 5, not 4, indicating that x is first incremented while x++ retains the value of 1 during evaluation of the expression. Both the x to the left as well as that to the right of x++ seem to contain a value of 2 during evaluation.
Which compiler do you use? It seems very likely to me that compiler differences have introduced these inconsistencies in our results.
Thanks a lot for putting in the time and effort to write this article. I think I finally understood a concept, which I was struggling with for nearly a year, in ten minutes.
LikeLike
Hi Taejas Gupta,
Thank you for sharing your results and your feedback! I am humbled that there are still people visiting and paying attention to this article despite putting up two years ago with some controversy, done purely based on experiments (using terrible programming practices), observations and some guess work.
Well I can’t remember what compiler I used, I definitely didn’t use C++. It has to be either Java or C# as I was teaching these two languages to my students.
Having said that, I must thank you for trying out my examples on a different platform. You allow me to learn and understand more about how post-increment works on different platforms and whether my hypothesis make any sense at all.
I agree with your observations that it does seems like there’s a different evaluation process that your compiler uses. Looking solely at your compiler alone, it seems that is still can produce deterministic results which showed that x++ is evaluated first some how.
I would say that not to worry about not producing the exact results though. “y = x++ + x” is a really terrible way to write a program after all. With that, I guess your results are within expectations too!
LikeLike
I maybe late for this but what does this mean for equtions like:
int x=2;
int y = x+x-x*3+8/x;
LikeLike
Hi RouxC#,
Pre-increment and post-increment will not affect the equation the code that you have just presented as there isn’t any. 🙂
They will following the usual mathematical order of operations, solving for * and / from left to right, followed by + and – from left to right.
Hope this helps~
LikeLike
int i=1;
printf(“%d %d\n”,(i++ + i),(i+ i++ + i));//Both expression will retain 5 even though the result of only the second expression is 5.
LikeLike
#include
#include
int main()
{
{
int a=1,b;
b=(a + a++);
printf(“1) a + a++ = %d\n”,a);
printf(“Conversion: 1 + 1 = %d\n\n”,(1 + 1));//1) There will be no effect on the operand at the time of its post increment.
}
{
int a=1,b;
b=a++ + a;
printf(“2) a++ + a = %d\n”,b);
printf(“conversion: 1 + 2 = %d\n\n”,(1 + 2));//2) The effect of post increment will be seen if the operand is respecified after (not before!) its post increment operation in an expression.
}
{
int a=1,b;
b=a + a++ + a + a;
printf(“3) a + a++ + a + a = %d\n”,b);
printf(“conversion: 2 + 1 + 2 + 2 = %d\n\n”,(2 + 1 + 2 + 2));//3) If same operand is specified one time before and any number of time after its post increment in an expression then the effect will be seen on both side. This is unexpected behaviour.???
}
{
int a=1,b;
b=a + a + a + a++ + a;
printf(“4) a + a + a + a++ + a = %d\n”,b);
printf(“conversion: 1 + 1 + 1 + 1 + 2 = %d\n\n”,(1 + 1 + 1 + 1 + 2));//4) But everything is normal if the same operand is specified multiple times before its post increment.
}
{
int c=1,d;
d= (c + c++ + c + c++ + c++ + c);
printf(“4.1) c + c++ + c + c++ + c++ + c = %d\n”,d);
printf(“Conversion: 2 + 1 + 2 + 2 + 3 + 4 = %d\n\n”,(2 + 1 + 2 + 2 + 3 + 4));//If an operand goes through multiple post increment operation in an expression, only the result of first operation affects the same operand which is respecified once before the first post increment operation. Otherwise the rules are same as information mentioned in the serial number ‘1’ and ‘2’.
}
{
int a=1,b;
b=a++ + a++ + a;
printf(“5) a++ + a++ + a = %d\n”,b);
printf(“conversion: 1 + 2 + 3 = %d\n\n”,(1 + 2 + 3));//If an operand goes through post increment operation, it will retain its previous value. But if the same operand is specified again preceding its post increment operation in an expression, it will retain the incremented value. Now if the operand holding incremented value goes through post increment operation, it will retain the previously incremented value. And the current incremented value will be seen in the further reuse of that operand. And this goes on.
}
{
int a=1,b;
b=(a++ + a) + (a + a++ + a);
printf(“3.2) (a++ + a) + (a + a++ + a) = %d\n”,b);
printf(“Conversion: (1 + 2) + (3 + 2 + 3) = 11\n\n\n”);
}
{
int i=1,j;
j=++i + i++ + i++ + i;
printf(“4) ++i + i++ + i++ + i = %d\n”,j);
printf(“Post: 2 + 1 + 2 + 3\n”);
printf(“Pre: 3 + 2 + 3 + 4 = 12\n\n\n”);
}
return 0;
}
LikeLike
Hi Simon,
That’s fantastic! Thank you so much for sharing. I really didn’t think of testing things it in this manner, chunking all the pre-increment and post-increment into a printf statement. That definitely brings lots of clarity to it.
You opened my eyes to a fresh set of ideas. : )
LikeLike