Programming glossary 37 terms mentioned in this lesson
Select any term for a clear definition. The return button brings you back to the exact term link you used.
Lesson: While & Do–While Loops — Non-Deterministic Iteration (C++)
For loops are ideal when the iteration count is known (or upper-bounded) at loop entry — even safer when it’s a compile-time constant. But many real tasks don’t have a known count in advance (e.g., reading an unknown-length file, user input until a sentinel, polling a device). That’s where while and do–while loops come in. They evaluate a condition each iteration and rely on state that changes during the loop to terminate. This power comes with risk: these loops can execute zero times or (worse) forever if the condition never becomes false.
Programming Insight (AI) — Pick the Right Loop
- Describe your task; ask AI to choose for vs while vs do–while and to state the loop invariant and termination condition.
- Have AI rewrite a fragile
while(true)into a condition-driven loop with a clear exit.
1) While vs Do–While — The Basics
- while: condition at the start — may execute zero times.
- do–while: condition at the end — executes at least once.
- Both rely on some control variable(s) changing each iteration so the condition eventually becomes false.
// while: may not run at all
int x = 0;
while (x < 5) {
++x;
}
// do–while: runs at least once
int y = 10;
do {
--y;
} while (y > 10); // condition checked after body
Programming Insight (AI) — Prove It Ends
- Ask AI to identify the variant (a measure that decreases/increases towards termination) for your loop.
- Have AI propose guard rails: max-iteration counters or timeouts, and where to place
break.
2) Classic Use Case — Sentinel-Controlled Input
Goal: Read integers until the user enters 0; print 100 + input for each.
#include <iostream>
int main() {
int value{};
// Loop continues while both extraction succeeds and sentinel not reached
while (std::cin >> value && value != 0) {
std::cout << 100 + value << '\n';
}
}
Why it’s correct: The condition both consumes input and tests it, so the loop progresses or ends. If input fails, the stream becomes false and the loop exits.
Programming Insight (AI) — Robust Input Patterns
- Ask AI to generate safe input conditions that both read and check (no stale values).
- Have AI add invalid-input handling: clear
std::cinand ignore bad tokens.
3) File Reading — Avoid the while(!in.eof()) Trap
The wrong way:
#include <fstream>
#include <string>
std::ifstream in("data.txt");
// ❌ buggy: eof() is only set after a read fails
std::string line;
while (!in.eof()) {
std::getline(in, line);
// last iteration may process stale 'line'
}
The right way: make the read the condition.
std::ifstream in("data.txt");
std::string line;
while (std::getline(in, line)) {
// use 'line'
}
Programming Insight (AI) — Generate Correct I/O Loops
- Provide your file format; ask AI for a correct read loop and minimal parser with error checks.
- Have AI add counters and limits to avoid pathological files causing endless loops.
4) Menu Loops — Why Do–While Shines
Goal: Show a menu at least once, then continue until user chooses Quit.
#include <iostream>
int main() {
char choice{};
do {
std::cout << "[A]dd [R]emove [Q]uit: ";
if (!(std::cin >> choice)) break; // input failed
switch (choice) {
case 'A': case 'a': /* add(); */ break;
case 'R': case 'r': /* remove(); */ break;
case 'Q': case 'q': std::cout << "Bye!\n"; break;
default: std::cout << "Unknown option\n"; break;
}
} while (choice != 'Q' && choice != 'q');
}
Programming Insight (AI) — Build a Menu Skeleton
- Ask AI to scaffold a do–while menu with input validation and a help option.
- Have AI factor actions into functions so the loop stays readable.
5) Escape Hatches — Guard Against Runaways
Non-deterministic loops need safety valves: a break on error, a max-iteration watchdog, or a deadline timeout.
#include <chrono>
bool work_once();
bool bounded_loop() {
using clock = std::chrono::steady_clock;
auto deadline = clock::now() + std::chrono::seconds(2);
int attempts = 0, max_attempts = 10000;
while (clock::now() < deadline && attempts < max_attempts) {
if (!work_once()) break; // escape on failure
++attempts;
}
return attempts > 0;
}
Programming Insight (AI) — Add Safety Valves
- Ask AI to add a watchdog counter or
steady_clockdeadline to an existing loop. - Have AI document the failure modes and what each
breakmeans.
6) Common Logical Errors (Why Novices Struggle)
- Condition never changes: forgetting to update the variable the condition depends on.
- Reading without consuming: testing a value but never reading new input → stuck on the same value.
- EOF pattern: using
while(!in.eof())instead of testing the read. - Continue + no update:
continueskips the update step you thought would run. - Stream failure: not checking
std::cin/ifstreamfailure clears; loop never progresses.
// ❌ infinite loop: condition never changes
int n = 5;
while (n > 0) {
std::cout << n << '\n';
// forgot: --n;
}
Programming Insight (AI) — Find the Stuck State
- Paste the loop; ask AI which variables must change each iteration and where to instrument them.
- Have AI propose a quick logging macro or debugger watch list for those variables.
7) Worked Example — Read Lines, Sum Numbers
Read an unknown number of lines; each line contains space-separated integers; sum all numbers.
#include <fstream>
#include <sstream>
#include <iostream>
#include <string>
int main() {
std::ifstream in("nums.txt");
if (!in) { std::cerr << "Cannot open file\n"; return 1; }
long long sum = 0;
std::string line;
while (std::getline(in, line)) { // progress: reads a line or exits
std::istringstream iss(line);
long long x;
while (iss >> x) { // progress: consumes tokens or exits
sum += x;
}
}
std::cout << "Total = " << sum << '\n';
}
Programming Insight (AI) — Verify Progress
- Ask AI to identify all progress points (what changes each iteration) and to add defensive checks/logs.
- Have AI generate small test files (empty, 1 line, malformed) to validate the loop behaviour.
8) Mini Exercise — Fix the Loop
Problem: This loop should read tokens until end of file, but it never stops on malformed input.
#include <iostream>
#include <string>
int main() {
std::string s;
while (true) {
std::cin >> s; // may fail and leave 's' unchanged
if (s == "quit") break;
std::cout << s << '\n';
}
}
Target fix: make the read the condition and handle failure:
int main() {
std::string s;
while (std::cin >> s) { // stops on EOF/failure
if (s == "quit") break;
std::cout << s << '\n';
}
}
Programming Insight (AI) — From while(true) to Correct Logic
- Ask AI to replace
while(true)with a condition that both consumes input and tests it. - Have AI suggest a sentinel, an error path, and a bounded retry policy.
Summary Checklist
- Use while / do–while only when the iteration count isn’t known at entry.
- Make the read/advance action the condition so the loop progresses or ends.
- Prefer do–while for menus and cases that must run once.
- Add escape code:
breakon error, watchdog counters, or timeouts. - Beware logical errors: stale values, unconsumed input,
eof()misuse, forgotten updates. - State the loop invariant and the termination measure—if you can’t, your loop is risky.
Advanced perspective: progress and termination
A while loop is governed by state, not unpredictability
Not knowing the iteration count in advance does not make a loop random. If a file contains an unknown number of integers, the program cannot write the count into its source, but the next input operation can still decide the path precisely. Run the same program with the same starting state and input, and you should expect the same decisions. The count was unknown before execution. The controlling evidence was not.
The real criterion is whether the loop exposes the state that permits one more iteration and the event that changes that state. A familiar while header is not enough. If the condition observes a fact that the body never changes, or an input operation fails without the failure being handled, the program can repeat a stale state indefinitely.
Test placement changes the minimum amount of work
| Form | Execution order | If the condition begins false | Suitable requirement |
|---|---|---|---|
while | Test, body, then return to the test | The body executes zero times. | Work is permitted only after the condition has been established. |
do-while | Body, test, then return to the body if true | The body executes once. | One attempt or presentation is required before continuation can be decided. |
A condition is evaluated at those test points; it is not monitored continuously while the body runs. In a while loop, a state change halfway through the body does not interrupt that body automatically. Control reaches the next test only through the program's stated control flow. In a do-while, remember that the final condition is followed by a semicolon because it completes the statement.
A menu illustrates the post-test case only when showing the menu once is genuinely required before deciding whether to show it again. Input processing often belongs in a pre-test loop because the body must not use a value until the read has succeeded. Choosing between the forms is therefore a decision about evidence before work, not a preference for one spelling.
Let the operation that obtains a value govern its use
#include <iostream>
int main() {
int total{0};
int value{0};
while (std::cin >> value) {
total += value;
}
std::cout << "Total: " << total << '\n';
}
The extraction expression does two connected jobs. It attempts to read and convert the next integer into value, and it produces a stream state that can be tested as a condition. When extraction succeeds, the condition admits the body and that newly obtained value is added. When extraction cannot produce the next integer, the stream tests false and the body is not entered.
| Input attempt | Condition result | Body action | total after the decision |
|---|---|---|---|
Read 4 | True | Add 4 | 4 |
Read 7 | True | Add 7 | 11 |
Read -2 | True | Add -2 | 9 |
| Attempt another read at end of input | False | Body is not entered | 9 |
The attempt matters. End-of-file state is normally discovered by trying to read beyond the available input, not by predicting before the last valid item that it is the last. That is why while (!input.eof()) asks the wrong question. It can admit the body before the read that discovers the end has succeeded, leaving the program tempted to process a value that was not obtained by that iteration.
Putting the extraction in the condition ties permission to use value to evidence that this extraction succeeded. The same reasoning applies to std::getline(input, line). It is not a magic idiom to memorise. It is a boundary: acquire a new item and enter the body only if acquisition succeeded.
Stopping on failure is not the same as handling failure
The compact program stops when no further integer can be extracted. End of input, text where an integer was required and a more serious stream failure can all lead out of the loop, but they do not necessarily deserve the same program response. If the requirement says that a completed file is normal but malformed input must be reported, the stream state must be examined after the loop and the cases distinguished.
Recovery is a separate policy. For malformed interactive input, the program may clear the failure state and deliberately discard or otherwise deal with the offending characters before trying again. Clearing the flags alone is not progress. If the same unacceptable characters remain available for the next extraction, the next attempt fails for the same reason and the loop has acquired a very efficient way to do nothing.
A sentinel adds another deliberate exit. In while (std::cin >> value && value != 0), short-circuit evaluation first requires a successful extraction. Only then is the value compared with zero. The sentinel is read, but it is not processed by the body. Failed input is not compared as though it were a new value. Those are distinct exit events, even if both leave the loop.
Progress must be stated in the terms of the problem
For a search with a finite set of unexamined elements, the number remaining can provide a loop variant: after an unsuccessful step, that number must fall. For a countdown, the control value approaches its bound. For an input loop, a successful iteration consumes an item, while exhaustion or failure prevents another body execution. For a menu, a recognised quit event changes the state that controls continuation.
Not every interactive or external loop can be proved to terminate independently of its environment. A service may be intended to wait for work until it is told to stop. An input loop may wait because a person never supplies a value or end signal. The honest claim is then conditional: when one of the specified exit events occurs, the loop detects it and follows the required path. Do not disguise an environmental assumption as a mathematical guarantee.
| Question | Evidence required | Typical defect |
|---|---|---|
| What permits another body execution? | A condition tied to current, valid state | The condition uses a stale value. |
| What changes during an iteration? | A state transition, consumed item or external event | The body repeats without progress. |
| What makes the condition false? | A reachable bound, failure, sentinel or stop event | The stated exit cannot be reached from the update. |
| What does exit mean? | A post-loop distinction between completion, sentinel and error where required | Every exit is silently treated as success. |
A safety boundary contains damage; it does not repair logic
An iteration limit, deadline or watchdog can be valuable when the requirement imposes a real work or time budget. It can also leave a diagnostic instead of allowing an unintended loop to consume resources without bound. However, reaching that boundary proves only that the safety mechanism acted. It does not prove the underlying algorithm made progress or completed its task.
A bare break has the same limitation. It changes control flow, but it does not make the original condition correct. Each break should correspond to an explainable event, and the code after the loop must know whether work completed, a sentinel was received, an error occurred or a safety budget was exhausted.
Programming Insight (AI): demand a progress and exit account
For every generated while or do-while loop, require the system to identify the state that permits another iteration, the operation that changes that state and every event that leaves the loop. Then trace an ordinary input, a condition that begins false and a failed input yourself. Watch for the word "eventually". Unless the explanation names the event or quantity that moves towards exit, "eventually stops" is hope dressed as analysis.
Trace failure before designing recovery
Use the input sequence 4 7 -2 x 9 with the worked program. Predict each attempted extraction and the total before running it.
Now decide what the requirement should do with x. Stop and report malformed input? Discard the rest of its line and ask again? Treat a specific token as a sentinel? Write the policy first, then identify the exact stream-state and character-handling operations needed to make the next attempt different from the failed one. Finally, test empty input and input beginning with x. If the first body execution is not justified by a successful read, the loop form is wrong before recovery even begins.
Reveal answer
The program adds the first three integers, then the attempt to convert x fails. The body is not entered for that attempt, the loop ends with a total of 9, and the later 9 is not processed.
The original loop makes four extraction attempts. The important row is the failed one: it does not authorise another execution of the body.
| Attempt | Extraction result | Body action | Total |
|---|---|---|---|
4 | Success | Add 4 | 4 |
7 | Success | Add 7 | 11 |
-2 | Success | Add -2 | 9 |
x | Failure converting to int | Body not entered | 9 |
The 9 remains unread because the failed stream state ends the worked loop. That is a correct trace, but it leaves the recovery policy unanswered.
One defensible interactive policy is to reject the malformed line, discard the rest of that line and continue with the next line. The program must clear the recoverable failure state and remove the offending characters. Clearing the flags without consuming input would merely repeat the same failure.
#include <iostream>
#include <limits>
int main() {
int total{0};
int value{0};
for (;;) {
if (std::cin >> value) {
total += value;
continue;
}
if (std::cin.bad()) {
std::cerr << "Input error\n";
return 1;
}
if (std::cin.eof()) {
break;
}
std::cerr << "Expected an integer; discarding this line\n";
std::cin.clear();
std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
}
std::cout << "Total: " << total << '\n';
}
For 4 7 -2 x 9 on one line, this chosen policy discards x and the remaining 9, so the final total is 9. If 9 begins the following line, the next extraction succeeds and the total becomes 18. The distinction is deliberate: recovery follows the stated line policy.
With empty input, the first extraction reaches end of input, no body action occurs and the total is zero. With input beginning with x, no value is processed before the failure is cleared and that line is discarded. A different requirement could stop and report the error instead, but it would need different code. The loop must not pretend that every failure has been recovered merely because execution continued.