Basics
Loops are one of the most basic fundamentals in programming, that allow us to repeat a certain block of code or simply put, some statements in the code. This repeatable block of code is called the loop body. In C, we commonly surround the loop body in curly braces { } .
Loops are executed until the condition set for the loop holds true. How we form those conditions is crucial to make sure our loops execute as intended, making our programs work as expected.
Loops are generally divided into entry-controlled and exit-controlled loops. the control type defines when the loop condition is checked, meaning when the decision is made whether to execute the loop body or not.
Different programming languages have different types of loops available. In C, we have while , do ... while and for loops.
Each type of loop has certain scenarios where they are preferable over others due to convenience and clarity. This does not mean, that the same cannot be achieved with a different type of loop.
While loop
This is the most basic and common loop type available. while loops are entry-controlled – the loop body only executes if the loop condition holds true.
The only mandatory component of a while loop structure is its condition – this controls if the loop body will be executed. It is checked on each iteration before executing the loop body.
1 2 3 4 |
while (condition) { // loop body } |
while loops are commonly used when the number of repetitions is unknown at the time of writing the code. This includes scenarios where the loop will never execute or will execute infinitely.
Example: display all powers of 3 below the limit value.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
// Set up the calculator int multiplier = 3; int product = 1; int limit = 2500; printf("Powers of %d under %d are\n", multiplier, limit); // Calculate the powers while (product < limit) { // print the current power printf("%d", product); // calculate the next power product = product * multiplier; } |
For loop
for loop is another type of loop that is entry-controlled, thus allowing for the possibility of never executing the loop body. for loops are most often used when when the number of repetitions is known beforehand.The structure of the loop has dedicated spots for initialization, condition and expression, separated by semicolons. This allows for clear and convenient use of counters. Make note of the location where each component of the loop is executed!
1 2 3 4 |
for (initialization; condition; expression) { // loop body } |
Example: The following loop will count from 0 to 9. Note that this is in the style of C90. Check below for difference between C90 and C99 (and later) style of writing.
1 2 3 4 5 6 |
int i; for (i = 0; i < 10; i++) { printf("i is %d\n", i); } |
Do … while loop
Do … while loop is the only loop in C that is exit-controlled, meaning that the body of this loop will always execute at least once, regardless of the condition.
1 2 3 4 5 |
do { // loop body } while (condition); |
This makes it convenient for tasks that need to be completed, but may need to repeat if some validation fails. An example of this would be user input, that needs to be validated before proceeding (i.e. user needs to enter a positive integer).
Example: The following loop repeats until the user enters . Notice that while userInp is left uninitialized, it’s not a problem for the loop. This is because the loop condition is checked after the user needs to enter a value for the variable.
1 2 3 4 5 6 7 |
int userInp; do { printf("Enter 0 to exit!\n"); scanf("%d", &userInp) } while (userInp != 0); |
C90 vs C99 loop declarations
ISO C90 forbids mixed variable declarations and code – all variables must be declared at the top of the function before any other code statements.
This forces you to declare loop iterators before the loop
1 2 3 4 5 6 7 8 |
void PrintIntArray(int nums[], int n) { int i; for (i = 0; i < n; i++) { printf("%d\n", nums[i]); } } |
Starting from C99 standard, you are allowed to declare the loop iterator right inside of the loop, making the code look a little cleaner.
1 2 3 4 5 6 7 |
void PrintIntArray(int nums[], int n) { for (int i = 0; i < n; i++) { printf("%d\n", nums[i]); } } |
Key takeaways
- For this subject, you can use both C90 and C99 style declarations.
- For some hardware, only C90 compilers will be available, forbidding you to declare the variable in the loop. This will cause a mixed variable declarations and code compilation error.
- When declaring in the C99 style, the loop iterator will be scoped to the inside of the loop only – you cannot access the i variable in the above example outside of the loop!
Infinite loops
Infinite loops are most commonly used when programming for embedded systems – they need to be repeating the same action infinitely over their lifetime. While again, any loop can be written as an infinite loop, the most clear way to do it is using the while loop with a condition of 1 . Any non-zero value will be evaluated as true in C. Typically you want to avoid using other types of loops when writing infinite loops!
1 2 3 4 5 |
// Infinite loop while (1) { // loop body } |
Sometimes you will also see infinite loops that allow for exit under certain scenario. For this, we use a conditional statement to the detect the event and a break statement to stop the execution of the loop.
1 2 3 4 5 6 7 8 9 10 11 |
// Infinite loop while (1) { // loop body // stop the loop if (exit-condition) { break; } } |
Note, that if the breaking condition is only in the end of the loop body, they can be easily written as do ... while loops – this is preferred option in C! An example of this is provided below:
1 2 3 4 5 6 |
// Infinite loop do { // loop body } while (!exit-condition); |
Nested loops
Nested loops is the practice of putting one loop inside of another loop. there is no functional limit how many loops can be nested inside of each other, but there are practical limits where the code will become unmaintainable. To avoid this, you should avoid nesting more than two loops. If you require to nest more loops, you should restructure the code into more functions.
There are also no requirements or limitations which types of loops can be mixed, i.e. you can put a while loop inside of a for loop.
In this example, we will nest two for loops in the C99 style – again, not a requirement.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
#include <stdio.h> int main(void) { int n = 5; int m = 7; for (int i = 0; i < n; i++) { printf("Start of the outer loop iteration i = %d\n", i); for (int j = 0; j < m; j++) { printf("i = %d; j = %d\n", i, j); } printf("End of the outer loop iteration i = %d\n\n", i); } } |
Note, that the outer loop contains the two print statements and a for loop. Both printf statements and the for loop are executed n times.
The inner loop uses a different counter j . The contents of the inner loop is executed m times. This causes the inner loop to be executed a total of n * m times.
Be careful on getting the conditions and counters correct when nesting loops! It’s very easy to mix up i and j and cause a loop to never execute or run infinitely.
Also note, that while using different counters and sometimes limits is common for many algorithms, it is not a rule! It depends on the algorithm you are implementing (i.e. the desired outcome).
Loops with no body
In C, a semicolon denotes the end of a statement. A semicolon can be written after the for or while loop header line, indicating, that the loop has no body. This can either be done on purpose, but it is also a very common mistake for beginner programmers.
As an exception, do ... while loops require a semicolon after the loop condition. This section does not apply to do ... while loops.
No body as a common coding mistake
When writing while loops, adding a semicolon after the condition will typically cause an unwanted infinite loop. In the example below, the loop iterator i will infinitely equal to , as the increment operation i++ is not a part of the loop body. While it is in the code block indicated by the curly braces, this block is not the body of the loop, but rather what comes after the loop.
1 2 3 4 5 6 7 8 9 |
int i = 0; // Mistake here causes an infinite loop while (i < 5); { // This IS NOT the loop body due to the semicolon! printf("i = %d\n", i); i++; } |
When writing for loops ending in a semicolon, we will similarly create a loop where the following code block is not its body. Most of the time it feels like either the loop is skipped or was only executed once, when in reality, it was executed in full.
In the following example, the loop will execute 5 times, which is as expected. However the only statement in the loop body is the increment i++ . Due to this, the loop will terminate as expected, compared to a similarly written while loop. However there will be no printout in the loop, as that statement is not within the loop body. The print statement is located in the code block after the loop and thus will be executed once.
1 2 3 4 5 6 7 8 |
int i = 0; // Mistake here will cause the loop to run "invisible" to the programmer for (i = 0; i < 5; i++); { // This IS NOT the loop body due to the semicolon! printf("i = %d\n", i); } |
No body as deliberate code
In some cases, programmers try to shorten their LOC (lines of code) by writing loops on a single line. This can be done for both while and for loops. This is typically considered in bad taste. The issue is that this kind of code is thought to be deliberately hiding intent (i.e. malicious code such as viruses). This will also cause issues with code review and debugging!
1 2 3 4 5 6 7 8 |
char str[] = "What a wonderful day!"; int i = 0; // Loop to the character past the space while (str[i++] != ' '); printf("The string from the second word: %s\n", &str[i]); |
While you should be aware of this, avoid writing code like this. Write the loop out in full, so that it’s easy to debug and maintain.
Note that this code will also crash the program if the string doesn’t contain a space – so we also created a hidden bug!