Iteration Debugging Exercises (15)
← Module home · Find → Fix → Reflect. Don’t compile first — trace. Every bug here is visible in a dry-run table (Lesson 1 §2); that is the skill being trained. Hints are progressive — open one only when stuck.
D1 — The tally that never ends
// intent: read 5 marks, print their total
int total = 0;
int i = 1;
while (i <= 5) {
int m;
std::cin >> m;
total += m;
}
std::cout << total << '\n';
What happens when you run it? Which rule-of-three part is broken?
Hints (open one at a time)
- Trace two passes: what does
ido in each? - The condition reads
i— who is supposed to changei? - The body updates
totalbut neveri— this is E1 of the error gallery.
Fix: i = i + 1; as the body’s last statement. Reflect: the condition tested a variable nothing updated — cause 1 of infinite loops.
D2 — The zero-pass sum
int total = 0;
int i = 5;
while (i <= 5) {
total += i;
i = i - 1;
}
std::cout << total << '\n'; // expected: 15 (5+4+3+2+1)
It compiles, runs, and prints 5. Why?
Hints
- Trace pass 1: what are
iand the condition’s verdict? - What is
ion the second check? - The update walks away from the exit — wrong-direction update (gallery E2).
Fix: i = i - 1; → i = i + 1; won’t fix it — the bound must flip too: while (i >= 1). Reflect: direction is a pair: update direction and condition direction must agree.
D3 — The overflowing loop
// intent: print 1..5, one per line
for (int i = 1; i <= 5; i = i + 1)
std::cout << i << '\n';
std::cout << "Done\n";
The program prints 1–5 — then prints Done five times… no. What does it actually print, and why?
Hints
- No braces — which statement belongs to the loop?
- What did Lesson 4 §6 call the semicolon’s cousin, the unbraced body?
- Only the first statement is the body;
Doneruns once — unless you misread the indent.
Fix: brace both statements. Reflect: indentation lies; braces don’t. This is the Decisions Lesson 1 braces rule applied to loops.
D4 — The off-by-one table
// intent: average of the 5 marks 10 20 30 40 50 (expect 30)
int total = 0;
for (int i = 1; i < 5; i = i + 1) {
int m;
std::cin >> m;
total += m;
}
std::cout << total / 5 << '\n';
With inputs 10 20 30 40 50, it prints… not 30. What and why?
Hints
- Which values does the body see for
i < 5? (E15’s pass sets) - The 5th mark is never read — where does it go?
i <= 5— but check the division too.
Fix: i <= 5. (And total / 5 is int division of a clean multiple here — 150/5 = 30, fine; with a non-divisible total it would truncate: use static_cast<double>(total) / 5.) Reflect: i < n vs i <= n — prove the boundary with the exit-sentence rule.
D5 — The poisoned accumulator
int total;
int i = 1;
while (i <= 3) {
int m;
std::cin >> m;
total += m;
i = i + 1;
}
std::cout << total << '\n';
Inputs 10 20 30 — output is 60 on one machine, 17409 on another. Same code, different answer. Why?
Hints
- What is
total’s value on pass 1? - Was it ever assigned?
- Uninitialized variable — gallery E4.
Fix: int total = 0; — accumulators start at their identity value. Reflect: “works on my machine” is the uninitialized-variable signature: the junk is whatever the memory held.
D6 — The vanishing total
// intent: running total of 4 sales
double total = 0;
for (int i = 1; i <= 4; i = i + 1) {
double sale;
std::cin >> sale;
double total = 0; // ← fresh start each pass?
total += sale;
}
std::cout << total << '\n';
Inputs 100 200 50 75 — prints 0. Find the two bugs.
Hints
- How many variables named
totalexist? - Which one does
coutsee? - Inner declaration shadows the outer — and resets every pass (gallery E5).
Fix: delete the inner line entirely. Reflect: declare accumulators once, before the loop; a declaration inside a loop is a reset, and shadowing hides the real one.
D7 — The hungry sentinel
// intent: total donations until -1
int total = 0;
int d;
std::cin >> d;
while (d != -1) {
total += d;
std::cin >> d;
}
std::cout << "Total: " << total << '\n';
The clerk enters donations 50, -1 and the total is 50 — correct. But a different clerk’s donations 30, -1, 20, -1 total 50 and the 20 vanished. Wait — re-read the intent: the clerk queues two customers’ donations separated by -1. What does this program actually compute, and what design question does that raise?
Hints
- What does the loop do at the first -1?
- Can it ever see the second customer’s 20?
- A sentinel ends the whole loop — it cannot mean “pause” (sentinel rules).
Fix (design, not syntax): one loop per customer — wrap the sentinel loop in an outer “next customer?” loop, or define a different data format. Reflect: a sentinel means “end of ALL data”. Nested data needs nested loops (Lesson 4 §1) — or a redesigned input format.
D8 — The self-colliding sentinel
// intent: average of quiz answers, each 1..4; stop on 0
int total = 0, count = 0;
int a;
std::cin >> a;
while (a != 0) {
total += a;
count += 1;
std::cin >> a;
}
if (count > 0)
std::cout << static_cast<double>(total) / count << '\n';
Answers 3 2 0 work. But the teacher’s sheet uses 0 to mean “question skipped” — a legitimate answer count. What went wrong before any code ran?
Hints
- What is a sentinel allowed to mean? (Lesson 3 §4)
- Can 0 ever be real data here?
- Sentinel choice is a requirements decision — gallery E6.
Fix: pick an impossible value: -1 (or read the count first and use a for). Reflect: the sentinel must be a value that can never be real data — that’s a property of the requirements, not the code.
D9 — The do-nothing loop
// intent: print Hello three times
for (int i = 1; i <= 3; i = i + 1);
{
std::cout << "Hello\n";
}
Prints Hello exactly once. Why?
Hints
- What is the loop’s body here? (gallery E7)
- What is the
{...}block to the loop? - The semicolon is an empty statement — the loop runs three times doing nothing.
Fix: delete the semicolon. Reflect: a semicolon after a loop header is a complete (empty) body; the block after it is just a statement that follows the loop.
D10 — The continue that ate the update
// intent: print 1..5, skipping 3
int k = 1;
while (k <= 5) {
if (k == 3) {
continue;
}
std::cout << k << '\n';
k = k + 1;
}
Prints 1 2 and then hangs forever. Trace it and fix.
Hints
- Pass 3: where does
continuejump to? (the trap) - Does
kget updated on that pass? - In a
while,continueskips the rest of the body — including the update.
Fix: move the update before the if, or convert to a for (whose update runs even after continue). Reflect: for honours the header contract; while’s update is just a body line.
D11 — The flat grid
// intent: 3 x 3 grid of stars
for (int r = 1; r <= 3; r = r + 1) {
for (int c = 1; c <= 3; c = c + 1) {
std::cout << "* ";
std::cout << '\n';
}
}
Output is 9 lines of one star each. Fix the structure.
Hints
- Which loop should own the newline? (grid model)
- Rows outside, columns inside.
'\n'after the inner loop, inside the outer.
for (int r = 1; r <= 3; r = r + 1) {
for (int c = 1; c <= 3; c = c + 1) {
std::cout << "* ";
}
std::cout << '\n'; // end of ROW
}
Reflect: the newline’s position encodes the row structure — Bug A of the grid pair.
D12 — The dependency that wasn’t
// intent: triangle — row r has r stars
for (int r = 1; r <= 4; r = r + 1) {
for (int c = 1; c <= 4; c = c + 1) {
std::cout << "* ";
}
std::cout << '\n';
}
Prints a 4×4 square, not a triangle. One character fixes it — which?
Hints
- What is the rule for row r? (rule → statement)
- The inner bound must depend on the outer variable.
c <= 4→c <= r.
Fix: c <= r. Reflect: a fixed inner bound is a rectangle; a rule that varies per row must appear as r in the inner condition.
D13 — The digit loop that won’t die
// intent: digit sum of n
int digitSum = 0;
while (n > 0) {
digitSum += n % 10;
}
std::cout << digitSum << '\n';
Hangs (or compiles into an infinite loop once n is declared and read). Trace a pass.
Hints
- What changes
n? % 10reads a digit — which operation removes it? (digit processing)- Missing
n = n / 10;— cause 1 again, now in digit clothing.
Fix: add n = n / 10; as the body’s last statement. Reflect: digit loops need both operations every pass — peel (% 10) and consume (/ 10).
D14 — The backward staircase
// intent: sum 1..n
int total = 0;
int i = 0;
while (i <= n) {
total += i;
i = i + 1;
}
n = 4 gives 10 — correct. But n = 0 gives 0 — also correct… and n = -3 gives 0 with no passes. So what’s wrong?
Hints
- Nothing — for
n >= 0. Now read the intent again: “sum of 1..n” for a user who enters -3. - Is a negative
nmeaningful input, or garbage? - Not every bug is inside the loop — this one is the missing validation guard (Decisions meets loops).
Fix: validate first: if (n < 0) { report and stop; } before the loop. Reflect: loops inherit input contracts — garbage in, confidently wrong out, unless a guard precedes the loop.
D15 — The double-count champion
// intent: highest of 5 marks
int max = 0;
for (int i = 1; i <= 5; i = i + 1) {
int m;
std::cin >> m;
if (m > max) {
max = m;
}
if (m >= max) {
max = m;
}
}
std::cout << max << '\n';
It prints the right answer for 40 55 91 60 70… and also for 0 0 0 0 0. So is it correct? What is wrong with it, honestly?
Hints
- Trace
40: pass 1 — whichiffires? Both? Does the outcome differ? - The two conditions overlap — the second is (almost) always true when the first is, and also when it isn’t (
m == max). - Redundant branches don’t create wrong answers here — they create fragile code and a hidden assumption (
max = 0seeding).
Fix: one condition if (m > max), and seed with the first value (if (i == 1) max = m; else if (m > max) ...) so negatives are handled (Lesson 2 §2). Reflect: the output looked right; the code was wrong twice — redundancy hides assumptions. Tests pass ≠ code is right.
Fix-list recap
| D | Bug family | Gallery link |
|---|---|---|
| D1 | missing update → infinite | E1 |
| D2 | wrong-direction update | E2 |
| D3 | unbraced body | E7 (cousin) |
| D4 | off-by-one bound | E3 |
| D5 | uninitialized accumulator | E4 |
| D6 | accumulator reset/shadowed in loop | E5 |
| D7 | sentinel misuse in nested data | E6 (design) |
| D8 | sentinel collides with data | E6 |
| D9 | semicolon body | E7 |
| D10 | continue eats while-update | E8 |
| D11 | newline in wrong loop | grid pair A |
| D12 | missing inner dependency | pattern rule |
| D13 | digit loop missing / 10 |
E1 (digit form) |
| D14 | missing input guard | validation |
| D15 | redundant overlapping branches | champion idiom |