1. Introduction

In this tutorial, we’ll explain the difference between the pre-increment and post-increment operators in a loop.

2. Pre-increment and Post-increment

Many programming languages such as Java or C offer specialized unary operators for the pre-increment and post-increment operations: ({++}operand) and (operand{++}). Both increment their argument variable by 1, but not the same way.

In particular, if x is an integer variable, and we pre-increment it as a part of a larger expression, the program will first add 1 to x and then evaluate x to the new value as the part of the evaluation of the entire expression. For example:

int x = 4; 
int y = (++x) + 100;
// x = 5, y = 105

In contrast, the post-increment alternative will increment x but evaluate it to the old value inside the expression. Only after the expression has been evaluated does x have the new value:

int x = 4;
int y = (x++) + 100;
// x = 5, y = 104

As we can see, x=5 in both cases, but the final value of y is different.

3. Pre-increment and Post-increment in a Loop

Let’s analyze how to use these operations inside a loop. We’ll focus on the loops with increasing counters.

3.1. The General Form of a Loop

Each counter-based loop is specified by:

  • its counter’s initial value
  • the termination condition
  • the increment step
  • and the loop’s body

Schematically:

algorithm GenericLoopForm():
    // This generic form demonstrates the structure of a loop
    counter <- initialize_the_counter()

    while termination condition is false:
        body()
        increment(counter)

If the termination test and the increment step are executed separately, it doesn’t matter which operator we use to increase the counter. The results will be the same.

However, if the termination condition test includes the increment operation, the two alternatives can lead to different results.

3.2. Testing the Condition With the Post-Increment

Let’s say that we want to print all the numbers between 1 and 10, inclusive. With the post-increment operator, we can try something like this:

algorithm PrintNumbersPostIncrement():
    // This algorithm prints numbers from 1 to 10
    // using post-increment in the condition
    
    i <- 0
    
    while i++ <= 10:
        print(i)

How does this loop unfold?

First, we set i to 0. Then, we check the condition i{++} \leq 10 by testing if 0 \leq 10 and updating i to 0+1=1 right after. Following the update, we execute the loop’s body.

Then, we repeat the iteration. We test the condition i{++} \leq 10. Since 1 \leq 10, we set i to 1+1=2 and move on to the print statement.

We go on like this until i=10 right before the test i{++} \leq 10. Since we’re working with the post-increment operator, we use the current value of i to check the condition and then add 1 to it. So, since i=10 before the check, and 10 \leq 10, i passes the test, which means we’ll execute the loop’s body. Because we use the updated counter after the test, we print 11. Then, we go back to the condition i{++} \leq 10. Since 11 > 10, we stop the loop.

The problem is that we’re printing \boldsymbol{11} even though we wanted only the numbers \boldsymbol{\leq 10}.

Therefore, we need to change the condition to i{++} < 10 or i{++} \leq 9. Unfortunately, that’s a bit counter-intuitive. If we test with < 10 or \leq 9, it isn’t clear straight away that we want to include the number 10 in the loop. The condition with \leq 10 would make more sense.

3.3. Testing the Condition With the Pre-Increment

Let’s rewrite the loop to use the pre-increment operator. For instance:

algorithm PrintNumbersPreIncrement():
    // This algorithm prints numbers from 1 to 10
    // using pre-increment in the condition
    
    i <- 0
    
    while ++i <= 10:
        print(i)

Will this loop include 11? Well, since we first increment i and then check if its new value is \leq 10, the last number we print will be 10. In the very next iteration, {++}i \leq 10 evaluates to 11\leq 10, which is false, so we end the loop.

3.4. Readability

Coupling the increment steps with the termination checks is a bad idea because the code gets less readable. Even though we made both loops work in the previous examples, we still needed to think about the last number and the correct formulation of the stopping criterion.

Also, although correct, the above loops don’t have an intuitive initialization step. Both initialize the counter as i \leftarrow 0. Until thoroughly analyzing the loops, most people would think that they print 0. In contrast, there’s no confusion with a loop with decoupled test and increment steps:

algorithm PrintNumbersWithSeparatedTestAndIncrement():
    // This algorithm prints numbers from 1 to 10,
    // separating the test and increment steps.
    
    i <- 1
    
    while i <= 10:
        print(i)
        i++ // ++i would also work here

Its meaning is immediately apparent. In general, we should strive for such a level of readability.

3.5. The Increment Step

As we said before, the pre-increment and post-increment operators don’t affect the semantics and correctness of our loop when we use them to increase its counter.

However, some object-oriented languages, such as C++, allow us to implement the {++} operators in our classes. Using such an object as our loop’s counter, we may see that the post-increment alternative runs slower than the pre-increment one.

The reason is that x{++} increments x but returns the old value. So, we need to store the new value until we evaluate the entire expression that x{++} participates in. The loop’s complexity won’t change, but if x is a complex object, it can run slower because of copying the content of x in each increment step.

4. Conclusion

In this article, we covered the difference between the pre-increment and post-increment operators inside a loop’s termination condition.

As a rule of thumb, we should decouple the increment step from the termination condition test to make our code more readable.