C · Beginner
Loops in C: for, while and do-while
In short: C has three loops: for when you know the count or need an index, while when you repeat until a condition fails, and do-while when the body must run at least once before the test. break leaves a loop immediately and continue jumps to the next test; a loop whose condition never becomes false runs forever.
Repeating a block
A loop runs a block of statements again and again while some condition holds. Which loop you choose depends on where the test sits and what you know in advance.
for (init; condition; step) gathers the three pieces of a counted loop in one line. init runs once before the first round, usually declaring the counter as in int i = 0, which C99 and later allow directly in the header. condition is checked before every round, including the first; if it is false the body is skipped. step, typically i++, runs after each round. A loop that counts from 0 while i < n therefore runs exactly n times. A counter declared in the header exists only inside the loop.
while (condition) checks the condition first and runs the body while it holds. Use it when the number of rounds depends on the data: reading values until the input runs out, halving a number until it reaches 1. The body must change something the condition depends on, otherwise the loop never ends. The idiom for reading all the integers from standard input is while (scanf("%d", &x) == 1): scanf returns 1 while it keeps finding numbers, and something else, such as EOF, when the input is exhausted.
do { ... } while (condition); puts the test after the body, so the body always runs at least once. Note the semicolon after the closing parenthesis; it is required.
Two statements alter a loop from inside. break ends the loop at once and execution continues after it. continue skips the rest of the current round and goes to the next test (in a for loop, the step runs first). Both apply only to the innermost loop they sit in. for (;;) and while (1) are the conventional spellings of a loop that exits only through break.
Loops nest: a loop over rows containing a loop over columns visits every cell of a grid, with the inner loop running to completion for each round of the outer one. Keep the counters distinct, and remember that the inner body runs rows times columns times.
Most loop bugs are off-by-one errors at the boundary: <= where < was meant, starting at 1 instead of 0, or a step that overshoots. When a loop misbehaves, write down the counter's value on the first round and on the last, and check the condition against both.
Syntax
for (init; condition; step) { /* init once; then test, body, step, test, ... */
body;
}
while (condition) { /* test first; may run zero times */
body;
}
do { /* body first; runs at least once */
body;
} while (condition); /* the semicolon is required */
break; /* leave the innermost loop now */
continue; /* skip to the next test (in a for loop the step runs first) */Any of the three parts of a for header may be left empty; for (;;) loops until a break.
Which loop
| You need to | Loop |
|---|---|
| Run exactly n times or walk indexes 0 to n - 1 | for |
| Repeat until the input runs out or a value crosses a limit | while |
| Run the body once before you can decide whether to repeat | do-while |
| Exit from the middle of the body | for (;;) or while (1) with break |
| Visit every cell of a grid | for inside for |
Counting with for
The first loop prints a ferry timetable every 15 minutes from 06:00 to 07:00 inclusive; the second counts down.
#include <stdio.h>
int main(void)
{
for (int minute = 360; minute <= 420; minute += 15) {
printf("%02d:%02d\n", minute / 60, minute % 60);
}
for (int i = 3; i > 0; i--) {
printf("%d ", i);
}
printf("go\n");
return 0;
}
Output
06:00 06:15 06:30 06:45 07:00 3 2 1 go
The counter does not have to step by 1 or count upward: the first loop adds 15 each round and uses <= because 07:00 itself should be printed, and the second subtracts. Each minute value is split into hours and minutes with / and %. The second loop prints on one line by leaving out \n, and the final printf ends the line. Both counters are declared in the header, so they are out of scope after their loops.
while over all the input
Five temperature readings arrive on one line, and the loop does not know in advance how many there are.
#include <stdio.h>
int main(void)
{
int reading;
int count = 0;
int total = 0;
int highest = 0;
while (scanf("%d", &reading) == 1) {
count++;
total += reading;
if (count == 1 || reading > highest) {
highest = reading;
}
}
if (count == 0) {
printf("No readings\n");
return 0;
}
printf("%d readings, total %d, highest %d\n", count, total, highest);
printf("Average: %.2f\n", (double) total / count);
return 0;
}
Input given to the program: 18 22 19 25 21
Output
5 readings, total 105, highest 25 Average: 21.00
scanf returns 1 each time it reads a number and something else when the input is exhausted, so the condition fails naturally at the end. The count == 1 || part makes the first reading the highest without needing a sentinel value; after that only larger readings replace it. The cast to double avoids integer division in the average. Give the program no input at all and count stays 0, which the if reports instead of dividing by zero.
do-while with continue and break
An oven runs numbered batches; every third batch is a cleaning slot, and the run stops after batch 7.
#include <stdio.h>
int main(void)
{
int batch = 0;
do {
batch++;
if (batch % 3 == 0) {
printf("batch %d: cleaning, skipped\n", batch);
continue;
}
if (batch > 7) {
break;
}
printf("batch %d: baked\n", batch);
} while (batch < 10);
printf("stopped after batch %d\n", batch);
return 0;
}
Output
batch 1: baked batch 2: baked batch 3: cleaning, skipped batch 4: baked batch 5: baked batch 6: cleaning, skipped batch 7: baked stopped after batch 8
The body runs first, so batch 1 is processed before any test. continue in a do-while jumps to the while test at the bottom, not to the top of the body, which is why batches 3 and 6 are skipped but still counted. When batch reaches 8, break leaves the loop before the print, so the message after the loop reports 8, not 7. The condition batch < 10 was never the reason the loop stopped.
Common mistakes
A semicolon after the loop header
Why it goes wrong:
for (int i = 0; i < 5; i++);is a complete loop with an empty body: it spins five times doing nothing, then the block in braces runs once.while (cond);with a condition that stays true hangs the program.Fix: Never write
;after the closing parenthesis of for or while. The only loop that ends with a semicolon is do-while.C · fixfor (int i = 0; i < 5; i++) { printf("%d\n", i); }Off by one at the boundary
Why it goes wrong:
for (i = 0; i <= n; i++)runs n + 1 times. With an array of n elements the last round reads past the end, which is undefined behaviour and may silently print garbage.Fix: Count from 0 and stop with
<; the loop then runs exactly n times, matching array indexes 0 to n - 1.A while condition that nothing changes
Why it goes wrong:
while (remaining > 0) { printf(...); }never touchesremaining, so the loop never ends and the output scrolls forever.Fix: Make sure the body moves toward the exit, for example
remaining -= batch;, or use for when the count is known.C · fixwhile (remaining > 0) { remaining -= 35; rounds++; }Using the counter after the loop
Why it goes wrong: A counter declared in the for header,
for (int i = 0; ...), does not exist after the loop;printf("%d", i)on the next line is a compile error.Fix: Declare the counter before the loop when you need its final value.
C · fixint i = 0; while (i < n && total < limit) { total += cost[i]; i++; } printf("stopped at %d\n", i);
Reading n, then n values
Many exercises begin with a line that says how many values follow. The shape is always the same: read n once, then a for loop that runs n times, reading one value per round and updating an accumulator such as a total, a maximum or a count of values that meet a condition. When the input has no count and simply ends, switch to the while (scanf(...) == 1) form. Nested loops handle the two-dimensional cases: reading a grid of rows and columns, or comparing every pair of values. Once you can write these three shapes from memory, the reading half of every exercise is solved and only the logic remains.
int n;
if (scanf("%d", &n) != 1) {
return 1;
}
int above = 0;
for (int i = 0; i < n; i++) {
int v;
if (scanf("%d", &v) != 1) {
return 1;
}
if (v > 100) {
above++;
}
}
printf("%d\n", above);Key points
forruns init once, then repeats test, body, step; the body may run zero times.whiletests first;do-whiletests after the body and always runs it once.while (scanf("%d", &x) == 1)reads every number until the input ends.breakleaves the innermost loop;continuejumps to its next test.- Count from 0 and stop with
<to run exactly n times. - Never put a semicolon after a for or while header; do-while needs one after its condition.
- A counter declared in the for header vanishes after the loop.
Try it yourself
Read two integers, rows and columns, and print a rectangle of # characters with that many rows and columns using two nested for loops. For the input 3 5 print three lines of five #.
#include <stdio.h>
int main(void)
{
int rows, cols;
if (scanf("%d %d", &rows, &cols) != 2) {
return 1;
}
/* outer loop over rows, inner loop over columns, newline after each row */
return 0;
}
3 5#####
#####
######include <stdio.h>
int main(void)
{
int rows, cols;
if (scanf("%d %d", &rows, &cols) != 2) {
return 1;
}
for (int r = 0; r < rows; r++) {
for (int c = 0; c < cols; c++) {
printf("#");
}
printf("\n");
}
return 0;
}
Practise this
Exercises for this lesson are in the C practice set.
Related lessons
- Conditions in C: if, else and switchHow C chooses between branches with if, else if, else and switch: what counts as true, combining tests with && and ||, and why each switch case ends with break.9 min
- Arrays in CHow C arrays store same-typed values by zero-based index: initialiser lists, the sizeof length idiom, passing arrays to functions and out-of-range dangers.10 min
- Functions in CHow to define and call C functions: prototypes before the first call, arguments copied by value, void functions, early returns and local variables.10 min
Frequently asked questions
How do I read input until there is no more in C?
Loop on the return value of scanf: while (scanf("%d", &x) == 1) keeps going while a number was read and stops when scanf returns EOF at the end of the input, or 0 when it meets something that is not a number. For whole lines use while (fgets(line, sizeof line, stdin) != NULL); fgets returns NULL at the end. Either way the loop needs no count up front.
Can I declare the loop variable inside the for header?
Yes, since C99: for (int i = 0; i < n; i++) declares i for the duration of the loop only. gcc with -std=c11 accepts it, and it is the recommended style because the counter cannot leak into or collide with code after the loop. If you need the counter's final value afterwards, declare it before the loop instead.
What is the difference between break and continue?
break ends the loop completely and execution resumes at the first statement after it. continue abandons only the current round: in a for loop the step expression runs and the condition is tested again; in while and do-while the condition is tested directly. Both affect only the innermost enclosing loop, and break also applies to switch, so a break inside a switch that sits inside a loop leaves the switch, not the loop.
How this page was checked. Every program on it was run with GCC 13 (C11) at build time by the publishing checks, and the output shown is what it printed. Running C inside the browser is not available yet, so the Run button is absent rather than pretending; copy the code and run it with GCC 13 (C11) locally.