Computer ScienceCore20 min read

Loop Constructs

while, do-while and for — and the off-by-one that costs a mark every year

This topic appears in:

01

Three loops, one decision

C offers three looping constructs. They are interchangeable in principle — anything one can do the others can — so the choice is about which expresses the intent most clearly.

Use for when the number of repetitions is known in advance, or when a counter is being stepped through a range. Use while when the loop continues until some condition changes and the count is unknown. Use do-while when the body must run at least once before the condition can be tested at all.

forwhiledo-while
Condition testedbefore each passbefore each passafter each pass
Minimum passes001
Best fora known countan unknown countmenus and input validation
Counterbuilt into the headermanaged by youmanaged by you
for (int i = 0; i < 10; i++) { … }/* initialise; test; update */while (condition) { … }/* test first — may run zero times */do { … } while (condition);/* test last — always runs at least once */note the semicolon after the closing bracket of do-while, and only there
02

The for loop in three parts

A for header does three separate jobs, separated by semicolons, and understanding when each runs removes most confusion about loops.

The initialisation runs once, before anything else. The condition is tested before every pass, including the first — so a for loop can execute zero times. The update runs after every pass, before the condition is tested again.

Step through for loop and watch i. It reaches 5 — one beyond the last value used — because the test at i <= 4 has to fail in order to end the loop.

The off-by-one

for (i = 0; i < 10; i++) runs ten times, with i taking 0 to 9. for (i = 1; i <= 10; i++) also runs ten times, with i taking 1 to 10. But for (i = 0; i <= 10; i++) runs eleven times, which is almost never what was wanted. When counting from 0 use <; when counting from 1 use <=.

03

while and do-while

A while loop tests before it runs, so it may execute zero times — which is usually correct. Processing a list of ten records should do nothing at all if the list is empty.

A do-while tests afterwards, so the body always runs at least once. That is exactly right for a menu, which must be displayed before the user can choose to leave it, and for input validation, where a value must be read before it can be checked.

Worked example

Write a loop that repeatedly asks for a mark until a value between 0 and 100 is entered.

  1. The prompt must appear at least once, so this is a do-while.A while loop would have to read a value before the loop as well, duplicating the input line.
  2. do { printf("Enter mark: "); scanf("%d", &marks);The prompt and the read are both inside the body, so they repeat together.
  3. if (marks < 0 || marks > 100) printf("Invalid\n");A message telling the user what was wrong, or they will simply type the same thing again.
  4. } while (marks < 0 || marks > 100);The condition describes when to KEEP LOOPING, which is the opposite of the acceptance rule — a frequent source of confusion.
  5. After the loop, marks is guaranteed valid.Reaching that line means the condition failed, so no further check is needed.

A do-while whose condition is the invalid range, so the loop repeats while the value is unacceptable.

04

Nesting, and the ways loops go wrong

Loops nest, and a loop inside a loop runs its inner body outer × inner times — nested tens execute the innermost statement a hundred times. Printing a table or processing a grid is the standard use.

Three faults recur. An infinite loop occurs when nothing inside changes what the condition tests. An off-by-one runs once too many or too few. And a misplaced statement — a total printed inside the loop rather than after it, or reset inside rather than before — produces output that looks almost right, which is worse than output that is obviously wrong.

break and continue

break leaves the loop immediately, which is useful when a search has found its answer and further looking is pointless. continue abandons the current pass and moves to the next. Both are legitimate but should be used sparingly: a loop with three exits is much harder to reason about than one whose condition says everything.

Before you leave this chapter

  1. for when the count is known, while when it is not, do-while when the body must run at least once.
  2. A for header initialises once, tests before every pass, and updates after every pass.
  3. Count from 0 with <, from 1 with <=. Mixing them gives the off-by-one.
  4. A while loop must change what its condition tests, or it never ends.
  5. Nested loops multiply: 10 × 10 executes the inner body 100 times.
05

Loops over arrays

The commonest use of a loop in C is stepping through an array, and the two are designed to fit together: an array of size n has indices 0 to n−1, and for (i = 0; i < n; i++) produces exactly those values.

That is why counting from 0 with a strict < is the standard C idiom. Using <= here would access marks[n], which is past the end of the array — and C will not stop you.

Worked example

Find the largest value in int marks[5].

  1. int largest = marks[0];Start with an actual element, not with 0 — starting at zero would give the wrong answer for an array of negative values.
  2. for (int i = 1; i < 5; i++) {Start at 1 because element 0 has already been used as the initial value.
  3. if (marks[i] > largest) largest = marks[i];Replace the running best only when a bigger value is found.
  4. } printf("%d", largest);Printed after the loop, or it would print a partial answer on every pass.
  5. Changing < to <= would read marks[5], outside the array.C performs no bounds checking, so the program would compare against whatever memory follows and may report a value that was never in the array.

Initialise from marks[0], loop from 1 to 4, and print after the loop.

Practice questions

6 questions · 20 marks · full working on every one

Try each one on paper first, then open the working. The marks are shown where they are actually awarded, because that is where they are actually lost.

Short questions

3 · 6 marks

Two marks each, in the style of the short-question section of the paper. Answer in two or three lines.

SQ1[2 marks]
State the key difference between a while and a do-while loop.
Model answer

A while loop tests its condition before the body, so it may run zero times. A do-while tests after the body, so it always runs at least once.

Examiner tip. The "at least once" phrase is the mark. Also note the do-while needs a semicolon after its closing bracket.

SQ2[2 marks]
How many times does for (i = 0; i < 5; i++) execute, and what is i afterwards?
Model answer

Five times, with i taking the values 0, 1, 2, 3 and 4. Afterwards i holds 5 — the test must fail for the loop to end, so i finishes one beyond the last value used.

Examiner tip. The final value of i is asked about often, and it is always one past the last one used by the body.

SQ3[2 marks]
Why is a do-while loop suitable for a menu?
Model answer

The menu must be displayed before the user can choose an option, including the option to quit. A do-while runs the body first and tests afterwards, so the menu always appears at least once.

Examiner tip. Input validation is the other standard example: a value must be read before it can be checked.

Solved numericals

2 · 8 marks

Full working, one step per line, with the marks shown where they are awarded.

N1[4 marks]
Write a C program that prints the sum of the numbers from 1 to 50.
Full working
  1. int total = 0; initialised before the loopinside the loop it would reset every pass[1]
  2. for (int i = 1; i <= 50; i++)<= because the count starts at 1 and 50 must be included[1]
  3. total = total + i; inside the loopaccept total += i[1]
  4. printf("%d", total); after the loop, giving 1275inside the loop it would print fifty lines[1]

total = 0 before, the for loop accumulating, and the print after — output 1275.

Examiner tip. Both misplacements are marked: initialising inside the loop, and printing inside it. Deciding what belongs before, inside and after is most of the question.

N2[4 marks]
This loop is intended to count down from 10 to 1 but never ends. Identify and fix the fault. int n = 10; while (n > 0) { printf("%d ", n); }
Full working
  1. Nothing inside the loop changes n[1]
  2. So n > 0 remains true for ever and the loop never terminates — an infinite loopnaming it is expected[1]
  3. Add n--; inside the loop bodyaccept n = n - 1[1]
  4. It must be inside the braces; placed after the loop it would never runthe placement point is a mark[1]

No decrement. Add n--; inside the loop body.

Examiner tip. For every while loop, ask what changes inside it that moves the condition towards false. If nothing does, it is infinite.

Long questions

1 · 6 marks

Theory and numerical together, as they appear in the long-question section.

LQ1[6 marks]
A program must read ten marks, count how many are 50 or above, and report the count and the average.
  1. State which loop is appropriate and why.
  2. Write the program.
  3. Explain two placement decisions that affect whether it works.
Mark scheme
  1. A for loop, because the number of marks is known in advance to be tenthe reason is required[1]
  2. int total = 0, passes = 0, marks; declared and initialised before the loop[1]
  3. for (int i = 0; i < 10; i++) { scanf("%d", &marks); total += marks;ampersand required[1]
  4. if (marks >= 50) passes++; }the test is inside the loop, applied to each mark[1]
  5. The accumulators must be initialised before the loop; inside, they would reset on every pass and the total would end as the last mark only[1]
  6. The printf statements must come after the loop, and the average must use total / 10.0 so that floating-point division is performedinteger division would truncate[1]

(a) for, since ten is known (b) accumulators before, loop reads and tests, output after (c) initialise before, print after, and divide by 10.0

Examiner tip. Three separate faults are possible here and all give plausible-looking output: resetting the total, printing inside the loop, and integer division. The last is the one that silently loses the decimal.