Module 6 · Loops and Jump Statements
while: the Idea of Repetition
In this lesson
- Write a
whileloop with a start, a condition and a step, and trace it with a table first. - Read numbers until a sentinel arrives, or until the input ends, with
while (scanf("%d", &x) == 1). - Explain what a missing part does, and keep the best value so far, starting from the first value read.
Bob's rocket game needs a countdown: 3, 2, 1, liftoff. He writes four printf lines and it works.
Then the designer asks for a countdown from 100. Bob does not want to write 101 lines, and he should not. One idea, "print the number, then make it smaller", has to run again and again.
A loop is a piece of code that runs again for as long as a condition allows. Computers never get bored of repeating, which is the whole reason they are useful.
while: the same block, again and again
A while loop tests a condition. If the condition is true, it runs the block once, then goes back and tests again. It stops the first time the test is false.
The while loop
while (condition) {
statements
}
whileis the keyword, and brackets always follow it.conditionis tested before every run of the block. Zero means stop; any other value means run once more.- The braces hold the body. One run of the body is called a pass (some books say an iteration).
- When the test is false, the program carries on after the closing brace.
#include <stdio.h>
int main(void)
{
int n = 3;
while (n > 0) {
printf("%d\n", n);
n--;
}
printf("liftoff\n");
return 0;
}
3
2
1
liftoff
Change the 3 to 100 for the long countdown. No other line changes.
So a while is an if that goes back to its own test after every pass. The body repeats until the answer turns into no.
Trace a loop with a table before you run it
The same lines run many times, so a loop is hard to read at a glance. Use a trace table (Module 5), one row per pass. Each row holds the variables at the test, the answer, and what the body did.
| Pass | n at the test | n > 0 | Body prints | n after n-- |
|---|---|---|---|---|
| 1 | 3 | true | 3 | 2 |
| 2 | 2 | true | 2 | 1 |
| 3 | 1 | true | 1 | 0 |
| none, test only | 0 | false | nothing; the loop ends | 0 |
Count the rows. The body ran 3 times, but the test ran 4 times. The last test, the one that says false, is the one that ends the loop.
The highlighted arrow is the back edge: the jump from the end of the body back to the test. An if has none, and that arrow is the whole difference between a decision and a loop.
So fill in the table on paper before you run a loop. If the last row never arrives, the loop never ends.
Start, condition, step: the three parts of every loop
Every loop that ends has three parts. The start gives the variable its first value. The condition decides whether another pass runs. The step changes the variable, so the condition can become false.
| Part | In the countdown | If it is missing or wrong |
|---|---|---|
| start | int n = 3; | The loop begins from the wrong place. Start n at 0 and the body runs zero times. |
| condition | n > 0 | Leave it out, while (), and nothing compiles. A condition that can never be false loops forever. |
| step | n--; | n stays 3, the test is true every time, and the loop never ends. |
The empty brackets are an error on every command line, the Playground included: error: expected expression before ')' token.
Note the zero-pass case. A while tests before the first pass, so its body may never run at all. Lesson 3 meets a loop that always runs at least once.
So when a loop misbehaves, check the three parts in order. Can the condition become false, and does the step move towards it?
Bob's timer that never ends
Bob writes a kitchen timer. It should count five seconds down and then beep. He rushes the step.
#include <stdio.h>
int main(void)
{
int seconds = 5;
while (seconds > 0) {
seconds - 1;
}
printf("beep\n");
return 0;
}
The line seconds - 1; works out 4 and throws it away, so seconds stays 5. The loop has no real step.
On the Playground nothing appears. After 10 seconds (the limit for a free or signed-out learner), the Playground stops the program. The status badge reads Time limit exceeded, and there is no output, not even the beep.
A judge, the program that grades problems against hidden tests, uses the same name. It usually allows 1 second per test. When it says Time limit exceeded, suspect a step that never moves towards false.
The Playground compiles Bob's timer in silence. A local gcc -Wall on GCC 12 says warning: statement with no effect [-Wunused-value]. The fix is seconds--;.
So a step has to store its result. A calculation that goes nowhere is not a step.
Stop at a sentinel: read until a 0 arrives
Maria's till reads prices, and the cashier types 0 when the basket is empty. A value like that 0, which means "stop" instead of "data", is a sentinel.
#include <stdio.h>
int main(void)
{
int price = 0;
int item = 0;
while (scanf("%d", &price) == 1 && price != 0) {
item++;
printf("item %d: %d\n", item, price);
}
printf("%d items\n", item);
return 0;
}
item 1: 120
item 2: 45
item 3: 300
3 items
That output is for the input 120 45 300 0 99. The 0 is the sentinel, and the 99 after it is never read.
The condition does two jobs, in order. First scanf reads a number and must return 1. Only then does price != 0 run, because && skips its right side when the left is false (Module 4).
The sentinel is read, fails the test, and never reaches the body. That is why "3 items" does not count the 0.
So a sentinel ends the loop and is never processed as data. Put the read and the sentinel check in the same condition.
Read until the input ends
Many judge problems have no sentinel. They say "numbers follow, up to the end of the input", and never say how many. That needs the pattern Module 3 promised.
#include <stdio.h>
int main(void)
{
int mark = 0;
int passed = 0;
while (scanf("%d", &mark) == 1) {
if (mark >= 40) {
passed++;
}
}
printf("%d passed\n", passed);
return 0;
}
3 passed
That output is for the input 72 35 90 on one line and 40 12 on the next. scanf skips spaces and line breaks alike, so the layout of the input does not matter.
Alice's program does not know how many marks are coming. It reads while reading works. After the 12, scanf finds nothing and returns EOF, which is -1 (Module 3, lesson 5). -1 is not 1, so the loop ends.
On the Playground, the end of the input is simply the end of what you typed in the input box. A judge's test file ends the same way. In a terminal on your own computer, press Ctrl+D on Linux and macOS, or Ctrl+Z then Enter on Windows.
With an empty input box the body runs zero times, and the program prints 0 passed. That is the right answer for no marks.
So while (scanf("%d", &x) == 1) means "for every number there is". It also stops at the first thing that is not a number.
Keep the best so far, starting from the first value
Zara logs the temperature every morning and wants the coldest. A loop can keep the best answer so far in a variable, and improve it on each pass.
Her input starts with the number of days. The first reading becomes the coldest so far, because at that moment it is the only one.
#include <stdio.h>
int main(void)
{
int days = 0;
int t = 0;
int coldest = 0;
int day = 1;
scanf("%d", &days);
scanf("%d", &t);
coldest = t;
while (day < days) {
scanf("%d", &t);
if (t < coldest) {
coldest = t;
}
day++;
}
printf("coldest: %d\n", coldest);
return 0;
}
coldest: 12
That output is for the input 4, then 18 12 25 14. Here day counts the readings taken, and there is always at least one day.
| Pass | day at the test | day < days | t read | coldest after the pass |
|---|---|---|---|---|
| before the loop | 1 | not tested yet | 18 | 18 |
| 1 | 1 | true | 12 | 12 |
| 2 | 2 | true | 25 | 12 |
| 3 | 3 | true | 14 | 12 |
| none, test only | 4 | false | nothing | 12 |
Why not start coldest at 0? Then no morning above freezing could beat it, and this input would print 0, a temperature nobody measured.
So a best-so-far variable starts from a real value, the first one read. Then the answer is always one of the inputs.
Braces on every loop body
Module 5 put braces on every if body, and loops follow the same rule. Without braces, a while owns exactly one statement.
#include <stdio.h>
int main(void)
{
int n = 3;
while (n > 0)
printf("%d\n", n);
n--;
return 0;
}
The compiler sees one line in the loop, printf, so the program prints 3 again and again. The Playground is silent. A local gcc -Wall says warning: this 'while' clause does not guard... [-Wmisleading-indentation].
So every loop body in this track gets braces, even a body of one line. Braces, not spaces, tell C where a body ends.
The step can be any change that moves towards false. Here it doubles.
#include <stdio.h>
int main(void)
{
int p = 1;
while (p < 100) {
printf("%d\n", p);
p = p * 2;
}
return 0;
}
1
2
4
8
16
32
64
After 64 the step makes p 128, and 128 < 100 is false. The loop ran 7 passes, a number nobody wrote in the program.
Kenji saves the same amount every week. Nobody knows the number of weeks in advance, which is when a while fits.
#include <stdio.h>
int main(void)
{
int goal = 0;
int weekly = 0;
int saved = 0;
int weeks = 0;
scanf("%d %d", &goal, &weekly);
while (saved < goal) {
saved = saved + weekly;
weeks++;
}
printf("%d weeks, %d saved\n", weeks, saved);
return 0;
}
7 weeks, 1050 saved
That output is for the input 1000 150. Six weeks give 900, which is short, so a seventh pass runs. A goal of 0 gives 0 weeks, 0 saved: zero passes.
Check the step, too: a weekly amount of 0 adds nothing, and the loop never ends.
Run in CompilerRead every number there is, and sort each one into a count. Many first judge problems have this shape.
#include <stdio.h>
int main(void)
{
int x = 0;
int even = 0;
int odd = 0;
while (scanf("%d", &x) == 1) {
if (x % 2 == 0) {
even++;
} else {
odd++;
}
}
printf("even %d\n", even);
printf("odd %d\n", odd);
return 0;
}
even 3
odd 2
That output is for the input 4 -7 10 3 0. The -7 is odd because its remainder, -1, is not 0 (Module 4). Here 0 is ordinary data, not a sentinel.
Where this is used
- Counting a file. GNU coreutils'
wcreads its input a block at a time in a loop. It stops when a read returns nothing: the end of the file. - Online judges. UVa problem 100, "The 3n + 1 problem", gives pairs of numbers up to the end of the file. The usual C solution reads them with
while (scanf(...) == 2). - Game loops. id Software published Doom's source code in 1997. The game runs inside
D_DoomLoop. Thatwhile (1)loop reads input and draws a frame on every pass. - Servers. Redis runs its event loop in
aeMainaswhile (!eventLoop->stop), handling the ready connections on each pass.
Common mistakes
1. Testing scanf without the == 1.
while (scanf("%d", &x)) {
printf("%d\n", x);
}
No message at any command line. At the end of the input scanf returns -1, which is not zero, so the test is true. For 7 9 it prints 7, then 9 forever. Compare with 1. You will write this because while (scanf(...)) reads like English.
2. A sentinel loop that ignores what scanf returned.
scanf("%d", &price);
while (price != 0) {
printf("item: %d\n", price);
scanf("%d", &price);
}
No message at any command line. If the input ends before a 0, the failed read leaves price as it was (Module 3). For 120 45 it prints item: 45 forever. Put the read in the condition. You will write this first because it is how the task sounds.
3. A semicolon after the while.
while (n > 0);
{
printf("%d\n", n);
n--;
}
Silent on the Playground. A local gcc -Wall gives the same -Wmisleading-indentation warning as above. The semicolon is an empty body, so the test repeats forever and the braces are never reached. Delete it.
4. The best so far starts at 0.
int coldest = 0;
while (day < days) {
scanf("%d", &t);
if (t < coldest) {
coldest = t;
}
day++;
}
No message at any command line. For 18 12 25 14 it prints coldest: 0. Start from the first value read. You will write 0 because counters start at 0, but a best-so-far is not a counter.
Bob's rocket game counts down from any number the player picks. Fix his countdown for real this time.
Input. One line with one integer n.
Output. The numbers from n down to 1, one per line.
Constraints. 1 <= n <= 1000.
Sample. Input 3 gives 3, 2 and 1 on three lines.
#include <stdio.h>
int main(void)
{
int n = 0;
scanf("%d", &n);
/* Start, condition, step: one loop, one number per line. */
return 0;
}
Not graded on its own. The problems lesson grades a while loop with harder edges in largest-input.
Maria's till now needs the bill. The cashier types the prices, then 0 when the basket is empty.
Input. Positive integers separated by spaces or newlines, then a 0. Anything after the 0 is ignored.
Output. One line with the sum of the prices before the 0.
Constraints. At most 1000 prices, each from 1 to 1000000, so the sum fits an int.
Sample. Input 120 45 300 0 gives 465. Input 0 gives 0.
#include <stdio.h>
int main(void)
{
int price = 0;
int total = 0;
while (scanf("%d", &price) == 1 && price != 0) {
/* Add this price to the total. */
}
/* Print the total. */
return 0;
}
Not graded on its own. Its one trap, the 0 slipping into the total, shows up the moment you run the sample.
Run in CompilerDavid's weather sensor writes readings to a log. He wants to know how many the log holds.
Input. Integers separated by spaces or newlines, up to the end of the input. There may be none at all.
Output. One line with the number of integers read.
Constraints. At most 100000 values, each from -1000000000 to 1000000000.
Sample. Input 5 -2 7 on one line and 0 3 on the next gives 5. An empty input gives 0.
#include <stdio.h>
int main(void)
{
int x = 0;
int count = 0;
while (scanf("%d", &x) == 1) {
/* One more reading. */
}
/* Print the count. An empty log must still print a number. */
return 0;
}
Not graded on its own. The graded largest-input has to notice an empty input in the same way, with a harder answer to print.
Zara feeds the scoreboard a list of scores and asks for the best. Scores can be negative, and some games have no scores at all.
Input. Integers to the end of the input, possibly none.
Output. One line with the largest, or empty when there were no numbers.
Constraints. At most 100000 values, each from -1000000000 to 1000000000.
Sample. Input 3 -8 12 12 5 gives 12.
#include <stdio.h>
int main(void)
{
int x = 0;
while (scanf("%d", &x) == 1) {
/* Keep the best score so far. What is it before any score arrives? */
}
/* Print the best score, or empty if nothing was read. */
return 0;
}
Graded as largest-input. The hidden tests include a list of only negative scores and a completely empty input.
Common doubts
When do I use while, and when for?
Use
whilewhen the number of passes is unknown in advance, as with input or a savings goal. The next lesson'sforsuits counting. Either can do the other's job; pick the one that reads more clearly.Is while (1) always a bug?
No. Doom's game loop is written that way on purpose. Such a loop leaves from inside, with
break, which lesson 4 teaches. In your programs today, it is almost always a bug.My program was stopped, and even the line it printed before the loop is missing. Why?
printfcollects output in a buffer and sends it on in pieces. On the Playground or a judge, the output goes to a file. A program that is stopped there never sends what was still waiting in the buffer.
Key takeaways
- A
whiletests its condition before every pass, and runs zero passes if the first test is false. - A trace table has one row per pass, plus a last row where the test turns false.
- Every loop needs a start, a condition and a step; without a real step, it never ends.
- A quiet loop that never ends runs until the time limit stops it, and the badge reads Time limit exceeded.
while (scanf("%d", &x) == 1)reads every number there is; a sentinel is checked in the same condition.- A best-so-far starts from the first value read, and every loop body gets braces.
Next, for puts the start, the condition and the step on one line, and Bob tries to print 1 to 10.
End of lesson 1
Mark it done, and your progress moves with you.
Next: for: Init, Condition, Update, Traced