Dr. Roger Ianjamasimanana

Loops in C: syntax and examples

By Dr. Roger Ianjamasimanana

For loop

The for loop is typically used when you know in advance how many times you want to repeat a block of code. It consists of three parts: initialization, condition, and increment/decrement.


#include <stdio.h>

for (int i = 0; i < 10; i++) {
    // code to be executed
}

2. While loop

The while loop is used when the number of iterations is not known beforehand, but you want the loop to continue as long as a condition is true.


#include <stdio.h>

int i = 0;
while (i < 10) {
    // code to be executed
    i++;
}

3. do-while loop

The ">do-while loop is similar to the while loop, except that the condition is checked after the loop body is executed.


int i = 0;
do {
    // code to be executed
    i++;
} while (i < 10);

4. Common mistakes

4.1 Infinite loops

Problem: a loop that never terminates.

Cause: incorrect logic in the loop condition or failing to update the loop variable.

Example

#include <stdio.h>

int i = 0;
while (i < 10) {
    printf("Hello\n");
    // Forgot to increment i, so the loop runs forever
}
Solution:
  • Make sure the loop variable is properly updated.
  • Use clear, simple conditions to avoid logical errors.
  • Add a break condition or timeout to prevent infinite loops.

4.2 Off-by-one errors

Problem: the loop executes one time too many or one time too few.

Cause: misplaced <, <=, >, or >= operators in the loop condition.

Example:

for (int i = 0; i <= 10; i++) {
    printf("%d ", i);
}

What’s wrong?>

This loop runs from 0 to 10, but if you only wanted it to go up to 9, you should use i < 10 instead of i <= 10.

Solution:
  • Double-check whether you need a less than (<) or less than or equal to (<=) in your loop condition.
  • Test edge cases to see if the loop runs as expected.

4.3 A loop that never runs

Problem: the loop body is never executed.

Cause: the loop condition is false from the beginning.

Example:

#include <stdio.h>

int i = 10;
while (i < 0) {
    printf("%d\n", i);
}
What's wrong?

The condition (i < 0) is false, so the loop will never run.

Solution:
  • Double-check the initialization and condition.
  • Consider if a do-while loop is more appropriate (since it guarantees at least one execution).

4.4 Array out-of-bounds access

Problem: looping beyond the size of an array.

Cause: incorrect loop conditions that allow access beyond array boundaries.

Example:

#include <stdio.h>

int arr[5] = {1, 2, 3, 4, 5};
for (int i = 0; i <= 5; i++) {  // This will cause out-of-bounds access on arr[5]
    printf("%d\n", arr[i]);
}
What’s wrong?

In C, arrays are 0-indexed, so accessing arr[5] is out of bounds. This can lead to undefined behavior.

Solution:
  • Use the correct loop range (i < 5 instead of i <= 5).
  • If possible, use sizeof(arr)/sizeof(arr[0]) to calculate the array size.
  • 4.5 Memory leaks (dynamic allocation in loops)

    Problem: forgetting to free dynamically allocated memory inside a loop.

    Cause: using malloc or calloc repeatedly in a loop without free.

    Example:
    
    
    #include <stdio.h>
    #include <stdlib.h>  // Required for malloc() and free()
    
    int main() {
        for (int i = 0; i < 100; i++) {
            int *ptr = (int *)malloc(sizeof(int)); // Allocate memory for an integer
            if (ptr == NULL) {
                fprintf(stderr, "Memory allocation failed\n");
                return 1; // Exit if memory allocation fails
            }
            *ptr = i; // Store the value of i in the allocated memory
            printf("Value at ptr: %d\n", *ptr); // Print the value
             // Forgot to free the allocated memory
        }
        return 0; // Return success
    }
    
    What’s wrong?

    Each loop iteration allocates memory but never frees it. This leads to a memory leak.

    What you should do:
    • Always call free(ptr) before re-allocating or before the loop ends.
    • Use valgrind to detect memory leaks.
    Updated code with a proper memory handling:
    
    
    #include <stdio.h>
    #include <stdlib.h>  // Required for malloc() and free()
    
    int main() {
        for (int i = 0; i < 100; i++) {
            int *ptr = (int *)malloc(sizeof(int)); // Allocate memory for an integer
            if (ptr == NULL) {
                fprintf(stderr, "Memory allocation failed\n");
                return 1; // Exit if memory allocation fails
            }
            *ptr = i; // Store the value of i in the allocated memory
            printf("Value at ptr: %d\n", *ptr); // Print the value
            free(ptr); // Free the allocated memory
        }
        return 0; // Return success
    }
    

    4.6 High CPU usage (busy-wait loops)

    Problem: the loop runs too fast and consumes all CPU resources.

    Cause: loops that don't pause or sleep cause 100% CPU usage.

    Example:
    
    #include <stdio.h>
    
    while (1) {
        // Busy wait
    }
    
    Solution:
    • Add a delay or sleep using usleep() or sleep() to give other processes a chance to execute.
    • Re-evaluate if a blocking I/O operation can be used instead of a busy-wait loop.

    4.7 Integer overflow in loop counter

    Problem: if the loop variable grows beyond the range of an integer, it "wraps around" (overflow).

    Cause: using int for a counter that becomes too large.

    Example:
    
    #include <stdio.h>
    
    for (int i = 0; i < 2147483647; i++) { 
        // Works for a while, but if you go beyond INT_MAX, it wraps around
    }
    
    Solution:
    • Use unsigned int or size_t if you expect large loop counters.
    • Be aware of the maximum size for int (usually 2,147,483,647 for 32-bit).

    4.8 Using uninitialized variables in loops

    Problem: forgetting to initialize loop variables, leading to undefined behavior.

    Cause: not setting the initial value of a counter or condition variable.

    Example:
    
    #include <stdio.h>
    
    int i;
    for (; i < 10; i++) {  // i is uninitialized, leading to unpredictable results
        printf("%d\n", i);
    }
    
    Solution:
    • Always initialize loop control variables before using them.
    • Use a debugger to identify uninitialized variables.

    4.9 Modifying loop control variable inside the loop

    Problem: changing the loop control variable in the body of the loop leads to unpredictable results.

    Cause: modifying i inside the loop body instead of letting the loop control it.

    Example:
    
    #include <stdio.h>
    
    for (int i = 0; i < 10; i++) {
        printf("%d\n", i);
        i += 2; // Modifying i inside the loop
    }
    
    Solution:
    • Avoid modifying the loop variable directly inside the loop body.
    • Use a secondary variable if necessary.

    4.10. Nested loops with high time complexity

    Problem: too many nested loops create performance bottlenecks.

    Cause: O(n^2), O(n^3), or worse time complexity.

    Example:
    
    #include <stdio.h>
    
    for (int i = 0; i < n; i++) {
        for (int j = 0; j < n; j++) {
            for (int k = 0; k < n; k++) {
                printf("%d\n", i + j + k);
            }
        }
    }
    
    Solution:
    • Avoid unnecessary nested loops.
    • Use efficient algorithms (like sorting, binary search, or hash maps) to reduce the number of iterations.

    4.11 Forgetting `break` in `switch` statements inside loops

    Problem: forgetting to use break in a switch inside a loop, causing unwanted multiple cases to execute.

    Example:
    
    #include <stdio.h>
    
    for (int i = 0; i < 3; i++) {
        switch (i) {
            case 0:
                printf("Zero\n");
            case 1:
                printf("One\n");
            case 2:
                printf("Two\n");
        }
    }
    
    What’s Wrong?

    Without break, all cases from 0 onward are executed, causing case 1 and case 2 to run unnecessarily.

    Solution: use break to exit the switch once a case is handled.

    4.12 Memory corruption

    Problem: accessing invalid memory in loops (especially with pointers).

    Cause: loop indexes that go out of range or pointers that go beyond array bounds. Example:

    
    #include <stdio.h>
    
    int arr[10];
    for (int i = 0; i <= 10; i++) { // i = 10 causes buffer overflow
        arr[i] = i;
    }
    
    Solution:
    • Use proper array index checks (i < 10 instead of i <= 10).
    • Avoid pointer arithmetic errors.

    5. Final tips

    • Use a debugger to step through loop iterations.
    • Use tools like Valgrind to detect memory issues.
    • Consider edge cases like 0, 1, and large values to ensure proper loop behavior.
    • Keep loops simple, clear, and well-commented.

feature-top
Readers’ comment
feature-top
Log in to add a comment