++i or i++?
If you are reading this blog, you are probably knowledgeable about embedded software and, therefore, like me, you consider yourself proficient in C. C is still the dominant language for embedded work. I read an article recently which espoused the view that “real men program in C” – I rather liked that. It also suggested that, since relatively few new graduates know C, there is an increasing shortage of expertise. This is great news for us old hands.
However, we should not be complacent. I think that I know C quite well. After all, it is quite a small language, so getting a grip should not be hard. Even aspects of it that are specifically interesting to embedded developers [and there are quite a few] should not be too difficult to master. But sometimes it is possible to get caught out …
I will pose a question: what is the difference between ++i and i++?
I am sure that you know that both expressions result in the variable i being incremented, but the first expression yields the incremented value if i, whereas the second returns the value prior to incrementing. OK so far. But what if the expression result is discarded in a situation like this, for example:
for (i=0; i<5; i++)
Does it make any difference which form of the increment operator is used in this context? I would have said no. But then I had a conversation with one of our compiler team and now I know better.
Functionally, it makes no difference – ++i and i++ would both do the right thing. However, embedded developers are interested in more than just functionality – we are always interested in overheads. Think about what code might be compiled for each form of the expression. For ++i, the variable is simply incremented; for i++, the value needs to be stored somewhere and then the variable is incremented. This means that the post increment form needs some additional storage, which is an unwanted overhead.
Of course, for a simple integer, the overhead is quite small. But, if the variable were actually a complex C++ object, it might be a more serious matter.
It can be argued that a good compiler would observe that the expression result is not needed and this code would be optimized away. I sincerely hope that this would be the case. However, it is sloppy programming to just rely on compiler optimization to address the ignorance or laziness of the programmer.
If you have any other examples of subtle C features that are interesting to embedded developers, please email me.
This very discussion arises quite regularly in code reviews in my company. Next time I’ll just forward a link to this article and save my blood pressure 🙂
Stephen: Good to hear that this is recognized in the “real world”. If you have any other issues that might be worth my discussing here, please email me: colin_walls@mentor.com
There’s also the possibility of actually having an assembly operator that makes more sense with one operation or the other. For example, out of my head I can think of DBCL (decrement and branch if clear). A good compiler would turn –i into that single instruction. I don’t think I can recall right now an instruction set that has that instruction in an increment version so I usually try to do for or while loops with predecrement, but there are indexing modes with post-increment that would make sense in other places. Anyway, it’s always good to also consider how what you are writing will translate into assembly.
By the way, that article has created so much hype that I’m also considering blogging about it.
Good input Eduardo. It raised a thought in my mind: If you want to go around a loop say 10 times and you don’t care about the direction of loop counting, doing it from 10 to 0 would be better, as the detection of zero is always easier than a comparison.
*** It can be argued that a good compiler would observe that the expression result is not needed and this code would be optimized away. ***
I believe that any reasonable C compiler since PCC (1974) will optimize this away. I was writing C compilers on three occasions and I cannot imagine a compiler company that would not optimize away i++ or even a->b[c].d.e->f ++.
It will not be optimized away if the value of the expression is used: a = b++, which is reasonable.
Maybe some compilers will have problems with expressions with side effects, like this:
a[f ()]++
or some compilers may have special semantics for “volatile” objects.
But other then this I don’t see much point in this discussion – IMHO anybody can safely assume that ((void) i++) alone is equivalent to ((void) ++i).
Yuri Panchul