For Loop in C

The for statement is one of the main flow control statements in C. It allows the creation of iterative loops of instructions.

The syntax of the for statement includes three parts: the initialization expression, the control expression that determines the continuation of the loop, and the end-of-iteration operation.

The for statement is particularly useful when you want to execute a block of code a known number of times. However, the for statement is so flexible that it can be used to construct virtually any kind of loop.

In this lesson, we will examine in detail the syntax and usage of the for statement to implement loops.

Key Takeaways
  1. The for statement allows you to create loops by specifying three expressions: the initialization expression, the control expression, and the end-of-iteration operation;
  2. Although the for statement is commonly used to execute statements a known number of times, its flexibility allows it to construct any type of loop;
  3. The three expressions are not mandatory;
  4. By using the comma operator, multiple expressions can be combined.

for Statement

The for statement is the most versatile—but also the most complex—looping construct in the C language. Its ideal use case is when there is a counting variable, but it can also be used for many other types of loops.

The for statement has the form:

for ( expression_1 ; expression_2 ; expression_3 ) statement;

To understand the purpose of the three expressions in the for statement, let’s revisit the countdown example that we already analyzed in the lessons on while loops and do while loops.

We want to print a countdown starting from 10. We can implement this functionality using a for loop as follows:

int n;
for (n = 10; n > 0; --n) {
    printf("%d\n", n);
}
printf("Liftoff!\n");

This for loop, in practice, performs the following steps:

  • Initializes the variable n to 10 using the first expression n = 10;
  • Checks whether the variable n is greater than zero using the second expression n > 0;
  • Since the condition is true, it executes the body of the loop;
  • After the body is executed, it modifies the variable n using the third expression --n;
  • It then re-evaluates the condition expressed by the second expression n > 0;
  • If the condition is still true, it executes the loop body again; otherwise, it exits the loop.

Therefore, the for loop is closely related to a while loop—so much so that any for loop can always be rewritten as a while loop.

In practice, a for statement of this type:

for ( expression_1 ; expression_2 ; expression_3 ) statement;

is equivalent to a while loop written as:

expression_1;
while (expression_2) {
    statement;
    expression_3;
}

From this structure, we can identify the purpose of the three expressions:

  1. expression_1 is the initialization expression: it is executed once before the loop starts;
  2. expression_2 is the control expression: it determines whether the loop continues. If true, the loop proceeds; otherwise, it ends;
  3. expression_3 is the end-of-iteration operation: it is executed at the end of each iteration, after the loop body.

Using this scheme, we can rewrite the example code as follows:

/* For loop format */
int n;
for (n = 10; n > 0; --n) {
    printf("%d\n", n);
}
printf("Liftoff!\n");

/* Equivalent while loop format */
int n;
n = 10;
while (n > 0) {
    printf("%d\n", n);
    --n;
}
printf("Liftoff!\n");

In general, the structure of a for loop with its three expressions follows this flowchart:

Flowchart of the for statement
Picture 1: Flowchart of the for statement

Since the initialization expression and the end-of-iteration operation are executed as statements, their return values are irrelevant. What matters is the effect they have. Typically, these expressions involve assignment, increment, or decrement operations.

Definition

for Statement

The for statement in the C language allows the implementation of iterative loops.

The general syntax is:

for ( init_expr; control_expr; end_expr ) statement;

The three expressions that appear in a for statement are:

  1. init_expr: the initialization expression, executed before the loop begins;
  2. control_expr: the control expression, evaluated at the start of each iteration. If it evaluates to true, the loop body is executed; otherwise, the loop ends;
  3. end_expr: the end-of-iteration expression, executed at the end of each iteration after the loop body but before checking the control expression again.

Typical for Loops

The for loop is often the ideal choice for loops that count, meaning those that increment or decrement a variable. A for statement that counts a number of times equal to n typically takes one of the following common forms:

  • Counting from 0 to n - 1:

    for (i = 0; i < n; ++i)
    
  • Counting from 1 to n:

    for (i = 1; i <= n; ++i)
    
  • Counting backward from n - 1 to 0:

    for (i = n - 1; i >= 0; --i)
    
  • Counting backward from n to 1:

    for (i = n; i > 0; --i)
    

The four patterns listed above are among the most common when programming in C. Using them helps avoid typical mistakes:

  • Using < instead of > and vice versa in the control expression. Note that when counting up, we used < or <=. Conversely, when counting down, we used > or >=.
  • Using the equality operator == in the control expression. This is often a mistake, because at the start of a for loop—as with a while loop—the control expression must evaluate to true. It becomes false to end the loop. So a control expression like i == n doesn't make much sense, because it wouldn’t be true at the beginning.
  • Off-by-one errors. These occur when you count one item too many and typically happen in for loops when the control expression is incorrect. For example, using i <= n instead of i < n.

Omitting Expressions in a for Loop

The for loop is very flexible. In fact, the three expressions in the for statement are not required.

There may be cases where not all three expressions are needed, and in C, it’s perfectly valid to omit one or even all of them.

If we omit the first expression, the for loop does not perform any initialization at the start. For example, going back to the countdown, we can rewrite the code without the first expression like this:

int n;
n = 10;
for (; n > 0; --n) {
    printf("%d\n", n);
}
printf("Liftoff!\n");

In this case, we performed the initialization of the variable n separately, so the first expression isn’t necessary. Note, however, that the semicolon must remain even if the expression is omitted.

If the third expression is omitted, the for loop does not perform any end-of-iteration operation. At this point, it becomes the responsibility of the loop body to ensure the control expression eventually becomes false.

Again using the countdown example, we can omit the third expression like this:

int n;
for (n = 10; n > 0;) {
    printf("%d\n", n);
    --n;
}
printf("Liftoff!\n");

We omitted the third expression but had to include the line --n. Without this line, the variable n would never be decremented, and the control expression would never become false.

Combining both of the above, if we omit both the first and third expressions, the for loop essentially becomes a while loop in another form.

Returning to the countdown example again, we can rewrite the for loop omitting both the first and third expressions as follows:

int n;
n = 10;
for (; n > 0;) {
    printf("%d\n", n);
    --n;
}
printf("Liftoff!\n");

But the code above is identical to the following while loop:

int n = 0;
n = 10;
while (n > 0) {
    printf("%d\n", n);
    --n;
}
printf("Liftoff!\n");

In these cases, however, the while loop is more readable and clearer, so it's generally preferable to use while.

Finally, if we omit the second expression, it is considered true by default. In this case, the for loop never terminates unless exited in some other way, as we’ll see in the next lessons. For example, it’s possible to create an infinite loop like this:

for (;;) {
    /* Infinite loop */
    /* ... */
}

for Statement and C99

In the C language, according to the C89 standard, variables must be declared at the beginning. Strictly speaking, in C89, variables must be declared at the beginning of the scope. In upcoming lessons, we’ll learn what a variable’s scope is. For now, it’s enough to know that in C89 a variable must be declared before any statement.

Based on this, if we take the countdown example, we must write the program like this:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
#include <stdio.h>

int main() {
    int n;
    for (n = 10; n > 0; --n) {
        printf("%d\n", n);
    }
    printf("Liftoff!\n");
    return 0;
}

In other words, we had to declare the variable n first on line 4.

Starting from the C99 standard, however, variables can be declared anywhere. So if we are using a C99 compiler (and nearly all modern compilers support the C99 standard), we can replace the first expression in a for loop with a declaration.

So we can rewrite the code above as follows:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
/* C99 Version */
#include <stdio.h>

int main() {
    for (int n = 10; n > 0; --n) {
        printf("%d\n", n);
    }
    printf("Liftoff!\n");
    return 0;
}

This way, the variable n is declared directly within the for loop, and there's no need to declare it beforehand. However, this also means that the variable n is not accessible outside the for loop. This is a preview of the visibility rules that we will study later.

Declaring the control variable of a for loop within the loop itself is always a good idea. As always, it improves program readability. However, if you need to access the control variable outside the loop, especially after it ends, you must use the older form of the for loop.

Comma Operator

The flexibility of the for loop in C also lies in the ability to use multiple expressions by employing the comma operator.

For example, it’s possible to write a for loop that initializes more than one variable or modifies multiple variables in each iteration.

To do this, the comma operator is used in the following form:

expression_1 , expression_2

Using the comma operator creates a new expression with its own result. The comma operator is evaluated in two steps:

  1. The first expression, expression_1, is evaluated, and its result is discarded;
  2. The second expression, expression_2, is then evaluated, and its result becomes the result of the entire expression.

For example, we can write the following lines of code:

int x;
x = (5 * 6, 7 + 8);

At this point, the program first evaluates the expression 5 * 6, and its result, 30, is discarded. Then it evaluates the expression 7 + 8, and its result, 15, becomes the result of the entire expression and, consequently, the new value of the variable x.

The comma operator has a lower precedence than all other operators, which is why, in the previous example, we had to enclose the expression in parentheses. In fact, if we had written the following code:

x = 5 * 6, 7 + 8;

The variable x would end up with the value 30, because the assignment has higher precedence than the comma operator.

The comma operator is also left-associative. Therefore, we can perform multiple assignments like this:

x = 1, y = 2, z = x + y;

In doing so, the compiler interprets the statement as follows:

((x = 1), (y = 2), (z = (x + y)));

So it proceeds like this:

  1. Assigns the value 1 to the variable x;
  2. Assigns the value 2 to the variable y;
  3. Evaluates the result of x + y, which is 3, and assigns it to z.

The comma operator can be used in situations where only one expression is allowed but you want to include multiple expressions. The operator allows us to glue two or more expressions together into a single expression. Similarly, curly braces allow us to group multiple statements into a compound statement.

The need to group multiple expressions does not arise often. However, for loops are the most common case where this is needed.

For example, suppose we want to write a program that calculates the sum of the first 10 natural numbers. We can write our program like this:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
#include <stdio.h>

int main() {
    int s;
    int i;

    s = 0;
    for (i = 1; i < 10; ++i) {
        s += i;
    }

    printf("%d\n", s);
    return 0;
}

In the program above, we initialized the variable s on line 7 separately. We can use the comma operator and write:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
#include <stdio.h>

int main() {
    int s;
    int i;

    for (s = 0, i = 1; i < 10; ++i) {
        s += i;
    }

    printf("%d\n", s);
    return 0;
}

Now, in line 7, using the comma operator, we initialized both variables. This makes the for statement more flexible.

To summarize:

Definition

Comma Operator

The comma operator , is an operator that allows multiple expressions to be combined into a single expression. The comma operator has the following form:

expression_1, expression_2

The comma operator first evaluates expression_1 and discards its result. It then evaluates expression_2, and its result becomes the result of the entire expression.

The comma operator has lower precedence than all other operators and is left-associative.

Summary

In this lesson, we studied the for statement, which allows the implementation of loops in a flexible way.

We saw that the syntax of a for statement includes three expressions:

  1. The initialization expression;
  2. The control expression;
  3. The end-of-iteration operation.

We also saw that none of these expressions are mandatory, and there are cases where they can be omitted. The flexibility of the for statement lies precisely in this, as by playing with the three expressions it’s possible to implement loops entirely equivalent to while loops.

Additionally, by using the comma operator, it is possible to create more complex expressions for building for loops.

In the next lesson, we will learn how to exit a loop.