When last did you see ++i
in a C, C++, C#, Java, or JavaScript program? On the other hand, you would have seen i++
numerous times. Why is this the preferred choice?
Operators vs Functions
Operators work very much like functions, in that they:
- Are passed arguments - which we call operands.
- Perform a task — the job/task that their names generally indicate e.g. addition.
- Return a value — the result of the task or calculation (not to be confused with its job/task).
The difference between functions and operators is thus in the syntax and terminology, rather than in the concepts. We use operator notation to call operators. And to be more efficient, since operators are so common in programming languages, the tasks they perform are expanded inline.
Side-effect Operators
Most operators do not affect the state of memory other than their own local variables, and their temporary return value. The few operators that do affect memory - i.e. change the state of the program before they return - are called side-effect operators. Most operators only require that their operands have a type and a value. But side-effect operators require more: their operands must also represent modifiable memory. Some languages refer to this requirement as lvalues.
The pre- and post-increment operators (and their cousins, the pre- and post-decrement operators,) require lvalues.
A simple example will illustrate this difference. The addition
operator (+
) is used on operands that have a numeric type and value - it can be used on literal values like 7
and 5
, or on variables that represent numeric values. The increment and decrement operators, on the other hand, can only be used on variables (i.e. modifiable memory) that represent numeric values. They cannot be used on numeric literals.
Pre-increment vs Post-increment
Now let's compare the pre-increment and post-increment operators.
The pre-increment operator:
- Requires an lvalue operand.
- Increments the actual memory of the operand (not a copy).
- Returns the new value.
The post-increment operator:
- Requires an lvalue argument.
- Make a copy of the current value.
- Increments the actual memory of the operand (not a copy).
- Returns the previous value (the copy saved).
As we can see, the only difference is at steps 3 & 4: they both perform the same job, but they return a different value. If the return value is used in code, this is significant. But if your code does not use the return value, then it makes no difference which operator you use. In fact, you will find that optimising compilers produce the exact same machine code in both cases.
Unless a compiler optimises your code, chances are that the post-increment code will require more instructions than pre-increment, and consequently be slower.
Just a Loop
So we ask the question: Why do we almost always see i++
instead of ++i
, particularly in loops?
Here's the simple answer: it has become a meme, a pattern that works and is passed from programmer to programmer, with no need to even think about it. That's fine - until you find that many will argue that the following two loops will execute differently:
for (int i = 0; i < 5; ++i) { …WORK… }
for (int i = 0; i < 5; i++) { …WORK… }
These two loops will produce identical results (WORK
is performed 5 times). Programmers who think they should execute differently do not completely understand either the rules of increment operator behaviour, or are vague about the sequence and operation of the for()
loop. What about the following set of for()
loops?:
for (int i = 0; i < 5; ) { …WORK…; ++i; }
for (int i = 0; i < 5; ) { …WORK…; i++; }
Considering that the expressions of the for()
loop are optional, we may omit the third. And since the for()
loop never uses the result of the third expression, these loops will perform exactly like the first set.
Here is another example with two while
loops, which are algorithmically the same as the above for
loops (semantically, there is a small difference regarding the scope of i
, which we mitigated to some degree with the enclosing compound statement):
{ │ {
int i = 0; │ int i = 0;
while (i < 5) { │ while (i < 5) {
…WORK… │ …WORK…
++i; │ i++;
} │ }
} │ }
Once again, these loops do not execute differently: WORK
is performed 5 times. Why are the results the same? In all the loops above, the return value of the operators is discarded and is not used. The only effect of the operators is the job they perform, and as we've seen, they perform the same job.
It's Your Choice
So which operator should you use when you do not care about its result (return value)? You are welcome to continue to use i++
, since it has become the de facto pattern since “The C Programming Language” by Kernighan and Ritchie. But as a programmer, you need to understand that in for()
loops, i++
is just a pattern, not a requirement or special syntax. And you could, on occasion, be a little daring and use that poor neglected ++i
in other situations where the return value does not matter.
1 thought on “Pre-increment vs Post-increment Operator in Loops”
Awesome blog you have here but I was wondering if you knew of any
user discussion forums that cover the same topics talked about in this article?
I’d really love to be a part of online community where I can get advice from other experienced individuals that share the same interest.
If you have any recommendations, please let me
know. Many thanks!