Week 2 of 8 · C++

Asking Questions and Doing Them Again

Why does God allow suffering — and is faith just intellectual?

Chapter 2 — Asking Questions and Doing Them Again

“…but test everything; hold fast what is good.” — 1 Thessalonians 5:21

“Train yourself for godliness; for while bodily training is of some value, godliness is of value in every way.” — 1 Timothy 4:7–8

This week merges Coding 1 chapters 3 and 4. If you also have the sixteen-week book, everything in “Asking Questions: Conditionals” and everything in “Repetition I: Loops” is here — resequenced into one week and taught as one idea instead of two.


Your Week at a Glance

Twelve honest hours, four sessions of about three. Slice them differently if you must, but do not try this in two sittings: the second half only makes sense once the first half is in your fingers, and fingers need sleep.

Session~TimeWhat you doDone when…
1 — Questions3 hrsRead §2.1–§2.9. Run if_else_demo.cpp, double_compare.cpp, bool_combinations.cpp, switch_demo.cpp, equals_bug_demo.cpp from code/. Do the conditional reps at the front of the exercises.You can write a five-branch cascade from a blank screen and say out loud why branch order decides the answer.
2 — Repetition3 hrsRead §2.10–§2.16. Run while_loop.cpp, do_while_demo.cpp, for_loop.cpp, break_continue.cpp, count_bounds.cpp, grid_demo.cpp, nested_loops.cpp, infinite_loop_debug.cpp. Do the loop reps.You can write all three loop forms from memory, and you have written one infinite loop on purpose and stopped it.
3 — Welding3 hrsRead §2.17; run conversation_loop.cpp three times down different paths. Read §2.18 with the file open. Finish every rep. Take the §2.20 Checkpoint.You pass the Checkpoint at 6 of 7 or better — or you re-drill instead of starting the project.
4 — The project3 hrsSketch your endings on paper first. Then build P2 — The Coffee Shop Conversation (Project 2) to Normal tier, compile clean with -Wall -Wextra, walk three different routes through it, submit.Your OnlineGDB link is submitted and you have personally reached three different endings.

Session 3 is not padding. Sessions 1 and 2 teach two toolsets; Session 3 is where they fuse, and the project needs them fused. Students who skip it spend Session 4 discovering they can write an if, and they can write a while, but have never written an if inside a while that changes the variable the while is watching. And if Session 4 runs long, it is almost always a design problem, not a coding problem — write the endings in plain English before you write a single cout.

One honest caveat about that last row. Project 2 estimates Normal tier at five to seven hours, not three. The three-hour block above is what the build costs a student who arrives having passed the §2.20 Checkpoint cold, with the endings already sketched. If that is not you, the extra two to four hours are real and you should schedule a fifth sitting for them rather than discover them at midnight on the due date. This is the one week where the twelve-hour figure is a floor instead of an average, and the Checkpoint in §2.20 is what decides which number you get.


Why This Matters

Your programs so far have a ceiling. They compute a thing, print it, stop. They cannot decide — cannot take one path when the milk is fresh and another when it is expired. And they cannot persist — cannot do a thing twenty times, or keep asking until the answer makes sense, or handle input whose length nobody knew in advance.

This week removes both ceilings at once. That is why these two topics share a chapter.

The mechanics are small: a handful of operators (<, ==, &&), four keywords that matter (if, else, while, for). The consequence is enormous. By Friday you can write programs that respond, branch, survive bad input, and run a whole conversation. You cross the line from calculator to program.

For the theme this book carries, the two halves are also the two halves of a life of the mind: asking well, and doing it again. Deciding well is hard; deciding well repeatedly — every day, when you are tired, when the question is uncomfortable — is harder. Your programs this week ask easy questions (is age >= 18?). Project 2’s question is not. Building the apparatus to handle easy ones cleanly is how you build the apparatus for the hard ones.

And the modern instinct to mistrust repetition as “rote” is simply wrong. A Bible-reading plan is a loop. A prayer practice is a loop. Physical training is a loop. Formation happens through repetition, in code and everywhere else. The keyboard is the gym.


2.1 — The Shape of a Question

Every conditional in C++ has one shape:

Compute a boolean. Then act on it.

The compute happens in an expression like age >= 18 or name == "Maya", which evaluates to true or false. The act happens in an if block:

if (age >= 18) {
    cout << "You can vote." << endl;
}

That is the whole idea. If the boolean is true, run the body; if false, skip it. The expression is the question, the body is the response.

You already built boolean expressions in Chapter 1 and printed them with boolalpha. The only new thing is that a boolean can now control what runs. It stopped being data and became a decision.


2.2 — Comparison Operators

To produce a boolean, compare two values. C++ gives you six operators:

OperatorMeaningExample
==equal tox == 5
!=not equal tox != 0
<less thanx < 100
<=less than or equal tox <= 100
>greater thanx > 0
>=greater than or equal tox >= 18

Three things to internalize before you write another line.

1. == is two equals signs. One equals sign is assignment: x = 5 puts 5 into x. Two is comparison: x == 5 asks whether x equals 5. Confusing them is the most common bug in beginner C++, and it gets its own section (§2.8).

2. Comparisons work on most types — not all the same way. Numbers behave as expected. A char compares by its integer code, so 'A' < 'B' is true. A std::string compares lexicographically by character code: given string a = "apple", b = "banana";, a < b is true.

But character-code order is not alphabetical order when case is mixed, because uppercase codes are smaller than lowercase in ASCII. With string z = "Zebra";, z < a is also true'Z' is 90, 'a' is 97. Case-insensitive comparison means lowercasing both strings first, which needs tools from Chapter 3.

Related trap, which the compiler now warns about: relational operators on bare string literals do not compare text at all.

cout << ("apple" < "banana") << endl;   // ❌ compares addresses, not letters

Put the text in std::string variables first, then compare.

3. Never compare doubles with ==. That gets its own section too.


2.3 — The Double Trap

Run code/double_compare.cpp. It takes no input. Its actual output:

a = 0.1 + 0.2
b = 0.3
a - b                  = 5.55112e-17
(a == b)               = false
abs(a - b) < 0.0001    = true

0.1 + 0.2 is not 0.3. It is 0.3 plus about 0.0000000000000000555. Doubles store values in binary, and one-tenth is a repeating fraction in binary the way one-third is in decimal. The leftover is not a bug in C++ or in your machine. It is arithmetic.

So == is the wrong question to ask about two doubles. The right question is “are these close enough?”:

#include <cmath>          // for std::abs

const double EPSILON = 0.0001;
bool roughly_equal = (std::abs(a - b) < EPSILON);

EPSILON is your tolerance — how far apart two values can be and still count as equal for this problem. 0.0001 is fine for money and ordinary measurements.

Short enough to memorize: integers and chars use ==; doubles use close-enough.

Coach’s Note — Everyone meets this once and feels cheated. Do not let it curdle into superstition (“floating point is unreliable”). Doubles are extremely reliable; they are simply not the real numbers you learned in algebra. Knowing the difference is part of the job.


2.4 — if, else if, else

The full vocabulary:

if (condition_1) {
    // runs if condition_1 is true
} else if (condition_2) {
    // runs if condition_1 was false AND condition_2 is true
} else {
    // runs if none of the above ran
}

Three facts:

  • else if and else are optional. A bare if is fine.
  • You may have as many else if branches as you like — zero, one, ten.
  • Exactly one branch runs. The moment a condition matches, the rest are skipped. If none match and there is no else, nothing runs and the program continues past the whole structure.

Here is the canonical example, and it is code/if_else_demo.cpp:

int score = 87;

if (score >= 90) {
    cout << "Grade: A" << endl;
} else if (score >= 80) {
    cout << "Grade: B" << endl;
} else if (score >= 70) {
    cout << "Grade: C" << endl;
} else if (score >= 60) {
    cout << "Grade: D" << endl;
} else {
    cout << "Grade: F" << endl;
}

Trace it with score at 87. >= 90 is false — skip. >= 80 is true — print Grade: B. Stop. The rest never run, even though 87 >= 70 and 87 >= 60 are also true. That is exactly what you want: the branches form a cascade, and the first true one wins.

Compile the file and feed it 87. Because the program prints a prompt and then reads, the answer lands on the same line as the prompt when input is piped rather than typed:

Enter a score (0-100): Grade: B

Feed it 55 and the last branch fires instead: Grade: F.

Coach’s Note — Order matters, and this is where most people get burned once. Had you written if (score >= 60) first, then >= 70, then >= 80, every passing grade in the course would land in the D bucket, because >= 60 catches them all before anything else gets a look. Cascades run most-restrictive to least-restrictive, or the reverse. When a cascade gives one answer for everything, check the order first.

Braces are not optional (they are, but they are not)

C++ lets you write a one-line if without braces: if (age >= 18) cout << "Adult." << endl;. That works. Now the famous bug:

if (age >= 18)
    cout << "Adult." << endl;
    cout << "You can vote." << endl;   // ⚠️ always prints

The second cout is not part of the if. An if without braces controls exactly one statement — the next one. Your indentation lied to your eyes; the second line runs whether the user is 8 or 80.

Modern g++ with -Wall flags this (§2.18), which is one more reason to compile with warnings on — but do not rely on it. Always use braces, even for one-line bodies. Two characters, one whole category of bug gone. It is the standard in professional C++ codebases; adopt it now, before you have a habit to unlearn.


2.5 — Combining Booleans: &&, ||, !

Real questions are rarely about one variable. Is the user logged in and at least 18? Is the score above 90 or is there extra credit? Is the answer not empty?

OperatorMeaningExample
&&AND(age >= 18) && logged_in
||OR(score >= 90) || extra_credit
!NOT!is_empty

The OR operator is two vertical bars, not one. A single | is a different operator entirely — bitwise OR — and while it often produces the right answer on booleans, it gives up the short-circuit behavior described below, which is sometimes the only thing keeping your program alive. Type both bars.

The rules are what you would guess. A && B is true only when both are; A || B when at least one is; !A flips it.

code/bool_combinations.cpp prints all of them for values you supply. Fed the age 20, the word yes, and the denominator 0, it produces exactly this:

Age: Verified (yes/no): old_enough                 : true
is_verified                : true
old_enough && is_verified  : true
old_enough || is_verified  : true
!is_verified               : false
Denominator: Cannot divide, or the quotient is 5 or less.

(Prompts and answers share lines because that run piped its input in. Typed by hand in OnlineGDB, your answers appear after each prompt.)

Short-circuit evaluation

When C++ evaluates A && B it evaluates A first. If A is false, it never looks at B — the result is already decided. Likewise for A || B: if A is true, B is never evaluated.

This is short-circuit evaluation, invisible until the day it saves you:

if ((denominator != 0) && ((100 / denominator) > 5)) {
    // ...
}

The division happens only if denominator != 0 is true. If the denominator is zero, the && short-circuits and the division — which on most systems kills your program — never runs. That is the second half of bool_combinations.cpp, and why feeding it 0 prints the “cannot divide” line instead of crashing.

Order matters here in a way it does not in arithmetic. ((100 / d) > 5) && (d != 0) is the same logic backwards, and it will crash. Guard first, then use.

Parentheses

C++ has precedence rules for !, comparisons, &&, and ||! binds tightest, then comparisons, then &&, then ||. Do not memorize them. Use parentheses liberally:

// Yes:
if ((score >= 90) || (extra_credit && submitted_on_time)) { /* ... */ }

// Strictly equivalent, but you must know the precedence table to read it:
if (score >= 90 || extra_credit && submitted_on_time) { /* ... */ }

Nobody has ever been marked down for clarity — and on this one the compiler is on the same side. Build that second line with -Wall and g++ warns you under -Wparentheses that it wants parentheses around the &&. Nothing is broken; C++ read the line the way you meant. The warning is the compiler saying that a human has to know the precedence table to be sure, and it is catalogued with the rest in §2.18. Add the parentheses and it goes quiet.


2.6 — Nested Conditionals

You can put an if inside an if:

if (logged_in) {
    if (age >= 18) {
        cout << "Adult dashboard." << endl;
    } else {
        cout << "Teen dashboard." << endl;
    }
} else {
    cout << "Please log in." << endl;
}

Sometimes that is right. Sometimes it is not. Compare:

if (logged_in && (age >= 18)) {
    cout << "Adult dashboard." << endl;
} else if (logged_in) {
    cout << "Teen dashboard." << endl;
} else {
    cout << "Please log in." << endl;
}

Same behavior, flatter shape. As a rule, flatter reads better, especially past two levels.

Coach’s Note — The “arrow anti-pattern” is a well-known smell: nested ifs drifting further right with each level until the code looks like an arrowhead pointing off the screen. It usually means you can combine conditions with &&, return early from a function (Chapter 3), or restructure the logic. The first time you see a five-level nest, you will smell it yourself.

But nest when the problem is nested. A follow-up question that exists only because of a prior answer is genuinely two levels deep, and flattening it makes it lie. Project 2 has exactly that shape: what your friend says next depends on what you said first. Nest there. Do not nest to show off.


2.7 — switch: A Different Shape

When you dispatch on a single value across many cases, an if/else if chain gets repetitive. switch is built for it. This is code/switch_demo.cpp:

switch (class_char) {
    case 'G':
        cout << "Gravitational force." << endl;
        break;
    case 'C':
        cout << "Cosmological constant." << endl;
        break;
    case 'E':
        cout << "Electromagnetic force." << endl;
        break;
    case 'W':
    case 'S':
        // Intentional fall-through: W and S are both nuclear forces.
        cout << "Nuclear force." << endl;
        break;
    default:
        cout << "Unknown class." << endl;
        break;
}

Fed W, it prints exactly:

Constant class (G/C/E/W/S): Nuclear force.

Fed Q, the default fires and it prints Unknown class. instead.

Four rules govern switch:

  1. Integer types onlyint, char, and relatives. Not double. Not string. String dispatch goes back to if/else if.
  2. Each case label must be a compile-time constant. case 3: and case 'A': are fine; case n: where n is a variable is an error.
  3. Each case must end in break; or execution “falls through” into the next case and keeps going. Forgetting break is a classic bug, and the compiler may say nothing.
  4. default: runs when nothing matched. It is the else of switch. Write one every time, even if it only prints “unknown” — an unhandled input that silently does nothing is a bug waiting for a grader.

Notice case 'W': with no body. That is intentional fall-through, the one good use of the behavior: two labels sharing one body. Use it sparingly and comment it every time, because the next reader cannot distinguish a deliberate fall-through from a forgotten break.

Which to use: switch when dispatching on one integer value across three or more cases. if/else if when each branch tests a different expression, or when you need ranges (score >= 90).


2.8 — The = vs == Bug

This will catch you, probably more than once. Learn it now, while it is cheap.

int score = 90;

if (score = 100) {           // ⚠️ BUG — one equals sign
    cout << "Perfect!" << endl;
}

That compiles. It runs. It always prints Perfect!. code/equals_bug_demo.cpp takes it apart one step at a time; its actual output:

score          = 90
(score == 100) = false

after (score = 100):
  score                   = 100
  value of the expression = 100
  is that value non-zero? = true
  ...so an if() built on it runs. Always.

Not perfect. score is still 90.

score = 100 is an assignment expression. It does two things: stores 100 into score, and evaluates to the value it assigned. The if then asks “is 100 truthy?” In C++ any non-zero integer counts as true, so the body runs — and you just silently overwrote your own data. A false positive and a corrupted variable, from one missing keystroke.

Modern g++ warns you if you compile with -Wall:

warning: suggest parentheses around assignment used as truth value [-Wparentheses]

Turn warnings on in OnlineGDB’s compiler options and this becomes a five-second fix instead of a two-hour hunt. Appendix A shows where that setting lives.

The habit that prevents it: read the line out loud. “score is 100” is a comparison — ==. “score becomes 100” is an assignment — =. If your mouth says “is,” your code says ==.

There is also a defensive style called Yoda conditions, putting the constant on the left: if (100 == score). A slip then produces if (100 = score), an immediate compile error, because you cannot assign to a literal. Some programmers swear by it; most modern codebases find it ugly and rely on the warning. Your call — just be consistent.


2.9 — Decision Style

Conditionals are the first place your code starts to look like you. Five habits worth forming now:

1. Test the common case first. Most users are logged in; most inputs are valid. Put the ordinary path where a reader finds it immediately.

2. Handle bad input and get out. Do not wrap your whole program in one giant if (valid) { ...300 lines... }. Deal with the failure, say something useful, move on. (Chapter 3 names this the guard clause, once you have functions to return from.)

3. Combine when it reads better. if (a && b) usually beats if (a) { if (b) { ... } }.

4. Do not combine when it would mislead. If the second condition is only safe because the first is true — the divisor check in §2.5 — the && is load-bearing, not cosmetic. Keep the order.

5. Avoid double negatives. if (!is_not_logged_in) is two negations to unpack; if (logged_in) is none. Name booleans positively.

Coach’s Note — A conditional is a question, and good questions are specific and have an obvious next move. Spend ten extra seconds naming your booleans. is_eligible beats flag2; friend_shared_pain beats b. The next person to read your code is your grader — and sometimes that person is you, in two weeks, with no memory of what you meant.


2.10 — The Bridge: A Loop Is a Question, Asked Again

Here is why these two topics are one week and not two.

Look at the while loop you are about to learn:

while (n < 0) {
    cout << "Enter a positive number: ";
    cin >> n;
}

Now look at an if:

if (n < 0) {
    cout << "Enter a positive number: ";
    cin >> n;
}

They are the same code, one keyword apart. The if asks its question once and moves on. The while asks the same question again after every pass and keeps acting as long as the answer stays true.

That is the entire relationship: a loop is a conditional that refuses to stop asking.

Everything you just learned transfers wholesale. A loop condition is a boolean expression, so it uses the same six comparisons, &&, !, and the same double trap. A loop can hold an if; an if can hold a loop. And the = versus == bug is worse inside a loop header, because a condition that is accidentally an assignment does not fire once — it fires forever.

A conditional decides once. A loop decides on every pass. Same machinery, different duration.

The rest of the chapter is about duration: the three shapes repetition comes in, the two variables you almost always carry through a loop, how to stop, and how to see inside a loop that will not.


2.11 — while and do…while

C++ has three loop constructs. They all repeat a block; they differ in shape, and the shape states your intent. Any loop can be written with any of them, but picking the one that fits is a small skill that separates a beginner from a programmer.

while — check first

int n = -1;
while (n < 0) {
    cout << "Enter a positive number: ";
    cin >> n;
}
cout << "Got it: " << n << endl;

C++ checks the condition; if true, runs the body; then checks again. When the condition is finally false, the loop exits and execution continues after the closing brace. Trace it: n starts at -1, the condition is true, the user types 5, the condition is now false, exit, print Got it: 5.

The thing to notice: if the condition is false on the first check, the body never runs at all. Sometimes that is exactly right — “keep going while there is work left” should do nothing when there is none. Sometimes it is a bug. Know which you meant.

Notice also the awkward part: n had to be initialized to -1, a meaningless value, purely to make the first check come out true. That smell is what the next shape fixes.

do…while — check after

Same idea, except the body always runs at least once, because the check is at the bottom:

int n = 0;
do {
    cout << "Enter a positive number: ";
    cin >> n;
} while (n < 0);

No fake sentinel needed. This shape fits whenever the first iteration behaves exactly like the rest — nearly the definition of prompting for input.

code/do_while_demo.cpp is the full version, with a counter and a range check. Fed 9, then 0, then 3:

Pick a number from 1 to 5:   9 is out of range. Try again.
Pick a number from 1 to 5:   0 is out of range. Try again.
Pick a number from 1 to 5: You picked 3 after 3 attempt(s).

Two things about do…while that bite people. The semicolon after the closing while (...) is mandatory — leave it off and g++ reports the error on the next line (see §2.18). And it is the least-used of the three, so do not reach for it first — but input validation is its home turf, and you will validate a lot of input in Project 2.


2.12 — Sentinels, break, and continue

A sentinel is a special value that means “stop.” You loop until you see it. It is how you read an amount of data nobody knew in advance. Here is the shape, from code/while_loop.cpp:

int total = 0;
int count = 0;
int input = 0;

while (true) {
    if (!(cin >> input)) {       // input ran out, or was not a number
        cout << "(input ended)" << endl;
        break;
    }
    if (input == -1) {           // the sentinel
        break;
    }
    total += input;
    count++;
}

Two constructs are new. while (true) is an intentional infinite loop that ends only when something inside says so. break; exits the nearest enclosing loop immediately, skipping the rest of the body and never rechecking the condition.

Fed 12, 15, 9, -1, it prints exactly:

Enter minutes practiced, one per line. Type -1 to stop.
Total: 36 minutes across 3 sessions.
Average: 12 minutes.

Run with no input at all — which is what happens in OnlineGDB when the Stdin box is empty — and it still ends cleanly:

Enter minutes practiced, one per line. Type -1 to stop.
(input ended)
Total: 0 minutes across 0 sessions.
Average: no sessions recorded.

That second run is not luck. It is the if (!(cin >> input)) guard doing its job — and the sidebar below explains why the guard must be there.

Coach’s Note — break is a real tool and it is easy to overuse. If the exit condition can be stated cleanly in the while (...) header, put it there — then a reader sees the loop’s whole life in one line. Reserve break for genuine mid-iteration exits: a sentinel, an error, a match found. A loop body sprayed with breaks is one nobody can reason about, including you.

continue

continue; abandons the rest of the current iteration and jumps back to the loop’s condition. It does not exit the loop. code/break_continue.cpp puts both in one place — negatives skipped, zero ends the run:

while (cin >> input) {
    if (input < 0) {
        skipped++;
        continue;   // ignore it, jump back to the condition
    }
    if (input == 0) {
        break;      // sentinel: stop now
    }
    total += input;
    kept++;
}

Fed 5 -3 10 -8 7 0, it prints:

Type integers. Negatives are skipped; 0 ends the run.
Kept 3 value(s), skipped 2 negative(s).
Total of positives: 22

5 + 10 + 7 is 22, two negatives were skipped, and the 0 stopped everything. One continue at the top of a loop body is clear; three scattered through forty lines is a maze.

Sentinel loops have a hidden hazard, and because you are working alone you need to recognize it from the symptom. Run a program that does cin >> input in a loop and type abc at the prompt. One of two things happens: the program ends abruptly, or it spins forever printing nothing.

What happened: cin >> input tried to parse abc as an integer, failed, and put cin into a failed state. Two consequences follow, and the second surprises everybody.

  1. cin stays broken. Every later cin >> input returns immediately without consuming anything. The offending abc is still in the input buffer, unread, blocking everything behind it.
  2. input gets set to 0. Since C++11, a failed extraction writes zero into the target. (Older tutorials say the variable is left untouched. That was true before 2011.)

Now look at the naive sentinel loop:

int input = 0;
while (true) {
    cin >> input;        // fails on "abc": input becomes 0, cin stays failed
    if (input == -1) break;
    total += input;      // adds 0. forever.
}

That loops forever. cin never recovers, input is pinned at 0, the -1 sentinel never matches, total quietly stops growing. Nothing prints. Nothing crashes. It hangs.

Three defensive moves, in increasing order of effort:

  1. Make the read the condition: while (cin >> input) { ... } evaluates to false on a failed read, so the loop ends cleanly. That is what break_continue.cpp does.
  2. Guard the read: if (!(cin >> input)) { break; } — same protection, and you can print a message on the way out. That is what while_loop.cpp does.
  3. Clear the failure and retry, if you want to give the user another chance:
    cin.clear();                                           // reset the failure flag
    cin.ignore(numeric_limits<streamsize>::max(), '\n');   // discard the rest of the line
    numeric_limits lives in <limits> — add #include <limits>.

Moves 1 and 2 are enough almost every time in this course. Carry this away: typing the wrong type at a cin prompt is a silent failure, not a crash. There is no message to search for. If a loop of yours hangs and prints nothing, suspect this first.


2.13 — for, Counters, and Accumulators

for — the workhorse

for (initialization; condition; update) {
    // body
}

Three parts, separated by semicolons. Initialization runs once, before anything else — usually setting up a counter. Condition is checked before each iteration; the loop runs while it is true. Update runs at the end of each iteration, after the body.

for (int i = 1; i <= 10; i++) {
    cout << i << " ";
}
cout << endl;

Trace it: i becomes 1; check i <= 10, true, print 1 ; update to 2; check, true, print 2 ; …until i is 11, the check fails, and the loop exits.

i++ means “increment i by one” — the same as i = i + 1 or i += 1, and the most common loop update in C++. The ++ operator has subtleties elsewhere; in a for header it is exactly as boring as it looks.

Coach’s Note — for is right about 80% of the time when you know the iteration count up front. while is right when you loop until something happens — input becomes valid, the user quits, the sentinel arrives. do…while is right when the first iteration is just like the rest. Read the loop’s intent and the shape picks itself.

Counter

A variable that changes by a fixed step each pass, often the loop variable itself. Counting down works the same way:

for (int countdown = 10; countdown > 0; countdown--) {
    cout << countdown << "..." << endl;
}
cout << "Liftoff!" << endl;

Accumulator

A variable that accumulates a result across iterations. Initialize before, update inside, read after:

int sum = 0;
for (int i = 1; i <= 10; i++) {
    sum += i;
}
cout << "Sum 1 to 10: " << sum << endl;

The same pattern gives you products (product *= i), running maximums (if (val > max) { max = val; }), counts, streaks, averages — anything with a “so far.” Three rules cover most accumulator bugs:

  • Counts and sums start at 0. Products start at 1. Start a product at 0 and it stays 0 forever.
  • Declare the accumulator outside the loop. Declare it inside and it is created fresh each pass, throwing away everything you accumulated. (The compiler usually catches the follow-on error when you read it afterward — §2.18.)
  • Guard the division at the end. total / count when count is 0 is a division by zero. Wrap it in if (count > 0). That is why while_loop.cpp’s empty run prints Average: no sessions recorded. instead of dying.

code/for_loop.cpp shows the accumulator with a built-in self-check: it computes 1 + 2 + … + n with a loop and with the closed-form formula n * (n + 1) / 2, then compares. Fed 100:

Enter a positive integer n: Sum via loop:    5050
Sum via formula: 5050
Match: true

That Match: true is the important line, and not because of the arithmetic. Two independent routes to the same number is how you grade your own loop when there is nobody to ask. Build that habit now — it is the most valuable single technique in a book written for people working alone.


2.14 — Nested Loops

A loop inside a loop. You need them whenever you work in two dimensions: rows and columns, days and slots, sets and reps.

code/grid_demo.cpp takes no input and prints both shapes. Actual output:

A 5 x 5 rectangle (inner bound is fixed):
* * * * * 
* * * * * 
* * * * * 
* * * * * 
* * * * * 
Inner body ran 25 times.

A triangle (inner bound depends on the outer variable):
* 
* * 
* * * 
* * * * 
* * * * * 
Inner body ran 15 times.

The rectangle comes from a fixed inner bound:

for (int row = 0; row < 5; row++) {
    for (int col = 0; col < 5; col++) {
        cout << "* ";
    }
    cout << endl;
}

The triangle comes from an inner bound that depends on the outer variablecol <= row. That dependency is the move worth learning; it builds anything shaped like a table or a pyramid.

Four rules:

  • The inner loop runs to completion on every outer iteration. In the rectangle it starts over and finishes five separate times.
  • Iteration counts multiply. 5 × 5 = 25. Add a third nested loop of 5 and the innermost body runs 125 times. A 1000 × 1000 nest is a million iterations, and you will feel it.
  • Use different loop variables. i and j is conventional; row and col is better, because the names say what they mean.
  • break exits only the innermost loop. Escaping a nested loop cleanly needs a flag (bool done = false; checked in both headers) or, from Chapter 3 on, a return out of a function.

code/nested_loops.cpp drives the same pattern from user input — outer loop over sets, inner loop drawing one star per rep. Fed 3 and 5:

Number of sets: Reps per set:   
Set 1: ***** (5 reps)
Set 2: ***** (5 reps)
Set 3: ***** (5 reps)

Total reps: 15 across 3 sets.

Run it again with 5 sets of 10. Same code, different work, because the bounds came from outside the program. That is the whole game.


2.15 — Off-By-One

The most famous beginner loop bug in any language:

for (int i = 0; i <= 10; i++) {
    cout << i << " ";
}

How many times does that run? Eleven. i takes the values 0 through 10 — eleven numbers, not ten.

code/count_bounds.cpp runs all three variants and counts them. Actual output:

for (int i = 0; i < 10; i++)   visits: 0 1 2 3 4 5 6 7 8 9  -> 10 iterations
for (int i = 1; i <= 10; i++)  visits: 1 2 3 4 5 6 7 8 9 10  -> 10 iterations
for (int i = 0; i <= 10; i++)  visits: 0 1 2 3 4 5 6 7 8 9 10  -> 11 iterations  (one too many)

Two idioms to memorize and then stop thinking about:

  • Counting from 0: for (int i = 0; i < n; i++) — runs n times.
  • Counting from 1: for (int i = 1; i <= n; i++) — runs n times.

Mixing them — starting at 0 and using <= — runs n + 1 times. That is the whole bug. It matters more than it looks: in Chapter 3 those bounds index into arrays, and an off-by-one stops being cosmetic and becomes a read past the end of memory.

Coach’s Note — Before you trust a loop, answer three questions out loud: what is the first value? what is the last value? how many is that? Thirty seconds of sketching buys back the thirty minutes you would otherwise spend next week wondering why the last item is missing.


2.16 — Infinite Loops, and How to See Inside Them

You will write a loop that never exits, probably this week. It happens to everyone. The only real cost is the ten minutes spent not knowing what to do about it.

Recognizing one. The console prints nothing and never finishes — your program is stuck. Or it prints the same output forever — the program runs, but the exit condition never changes.

Stopping it. In OnlineGDB, the Run control becomes a Stop control while your program is running; click it. If the page is unresponsive from a flood of output, reload the browser tab — the runner dies with it. In a local terminal, press Ctrl+C (the Control key, not Command). Nothing is damaged.

The three causes. (1) The condition never becomes false — you wrote while (i < 10) and forgot i++. (2) You updated the wrong variable — while (i < 10) { j++; }. (3) while (true) and the break never fires, often because of the cin failure in §2.12.

Seeing inside. The technique that solves all three: print the loop variable at the top of the body.

while (i < 10) {
    cout << "top of loop, i is " << i << endl;
    // ... rest of body
}

Now the bug is visible in one run. If i prints 0 forever, it is cause 1 or 2. If i climbs but nothing exits, your condition is wrong.

code/infinite_loop_debug.cpp demonstrates that plus one more trick — a safety valve, a hard iteration cap on a while (true) loop. Its actual output:

Part 1: trace print at the top of the body.
  top of loop, i is 0
  top of loop, i is 1
  top of loop, i is 2
  top of loop, i is 3
  top of loop, i is 4
  loop finished with i = 5

Part 2: a safety valve on a while(true) loop.
  value = 23 after 6 pass(es).

The valve looks like this:

const int MAX_ITERATIONS = 1000;
int guard = 0;

while (true) {
    guard++;
    if (guard > MAX_ITERATIONS) {
        cout << "SAFETY VALVE: exit condition never fired." << endl;
        break;
    }
    // ... the real body and the real exit condition
}

Put one in any while (true) loop you are unsure of while developing. It converts a hang — which tells you nothing — into a message that tells you exactly what went wrong. Take it out, and the trace prints with it, once the loop is right.


2.17 — Worked Example: Both Halves at Once

code/conversation_loop.cpp is Project 2 in miniature, and every idea in this chapter is in it: a while (true) loop keeping the scene alive, an if catching the sentinel (0 ends it), an if catching out-of-range input and using continue so a typo does not cost a turn, a guarded read so the program ends cleanly when input runs out, a switch dispatching on the choice, an accumulator (warmth) remembering the whole run, a counter (turns) limiting it, two boolean flags, and a final if/else if cascade with a && in it that picks the outcome. The core:

while (true) {
    if (turns >= MAX_TURNS) {
        cout << endl << "The barista calls last orders." << endl;
        break;
    }

    // ... print the turn prompt ...

    int choice = 0;
    if (!(cin >> choice)) {
        cout << endl << "(the conversation is interrupted)" << endl;
        break;
    }
    if (choice == 0) {
        cout << endl << "You let it rest." << endl;
        break;
    }
    if (choice < 0 || choice > 3) {
        cout << "  That was not one of the options. Try again." << endl;
        continue;   // no turn is spent on a typo
    }

    turns++;

    switch (choice) {
        case 1:
            warmth += 2;
            asked_something = true;
            break;
        // ...
    }
}

And the ending is a cascade over accumulated state, not over the last thing the user typed:

if (turns == 0) {
    // the moment passed
} else if ((warmth >= DEEP_THRESHOLD) && asked_something) {
    // you both went deeper
} else if (cut_them_off) {
    // you answered a question nobody finished asking
} else if (warmth >= 2) {
    // nobody won anything, which was the point
} else {
    // you stumbled, and said so
}

That distinction is the design lesson of the week. A program that decides your ending from your last choice is a menu. A program that decides from what you did across the whole run is a story. The accumulator is the difference, and it exists only because there is a loop.

Here is a real session, produced by feeding it 1, 3, 9, 1, 0 — where 9 is a deliberate typo, to show validation working. Exact output; prompt and response share a line because the answers were piped rather than typed:

*** The Coffee Shop ***
Your friend sets down the cup. "If God is good, why is there
so much suffering?" It is not a debate move. Something happened.

Each turn: 1 = ask, 2 = answer, 3 = stay silent, 0 = let it rest.

Turn 1 of 4 >   You ask. Your friend keeps talking, longer than you expected.

Turn 2 of 4 >   The silence is not awkward. Your friend fills it.

Turn 3 of 4 >   That was not one of the options. Try again.

Turn 3 of 4 >   You ask. Your friend keeps talking, longer than you expected.

Turn 4 of 4 > 
You let it rest.

=== How it landed ===
turns taken: 3   warmth: 5

You both went deeper. Quieter, less debate-shaped.
You make plans to keep talking. It is not finished.

Look at the counter after the typo: Turn 3 of 4 appears twice. That is continue doing its job — the input was rejected and the turn counter never advanced.

Now feed it 2, 2, 2, 2 — answer every time, ask nothing — and the tail becomes:

=== How it landed ===
turns taken: 4   warmth: -4

You answered a question your friend had not finished asking.
They let you finish. The moment is gone. You notice, later.

Same code. Different path. That is what you are building this week.


2.18 — Common Bugs (Week 2 Edition)

Every message below is real g++ -Wall -Wextra output, the same compiler family OnlineGDB runs. File names, line numbers, and column markers are stripped; yours will differ. Read the first error, not the last. C++ diagnostics cascade — one missing brace on line 12 can generate nine complaints about line 40, and fixing the first usually deletes the rest.


Bug: An if body runs no matter what the condition is.

warning: suggest parentheses around assignment used as truth value [-Wparentheses]
    if (score = 100) {
        ~~~~~~^~~~~

Means: You wrote = where you meant ==. It is an assignment, it evaluates to the assigned value, and non-zero is true (§2.8). Fix: Change = to ==. Note this is a warning — with warnings off, the bug is completely silent.


Bug: A combined condition gives the right answer, and the compiler complains anyway.

warning: suggest parentheses around '&&' within '||' [-Wparentheses]
    if (score >= 90 || extra_credit && submitted_on_time) {
                       ~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~

Means: Nothing is broken. && binds tighter than ||, so C++ grouped it the way you almost certainly intended. The compiler is flagging that a human reading the line has to know the precedence table to be sure (§2.5). Fix: Add the parentheses it is asking for — if ((score >= 90) || (extra_credit && submitted_on_time)). This is the one entry in this list about readability rather than correctness. Fix it anyway: you want a build with zero warnings, because that is the only way the real warnings stay visible.


Bug: A do…while will not compile, and the error points at a line that looks fine.

error: expected ';' before 'cout'
    } while (n < 0)
                   ^
                   ;

Means: The semicolon after do { ... } while (condition) is mandatory. The compiler kept reading and complained at the next statement. Fix: Write } while (n < 0);. Whenever an error says “expected ;” and the named line looks correct, check the line above.


Bug: A switch on a string will not compile.

error: switch quantity not an integer
    switch (answer) {
            ^~~~~~

Means: switch takes integer types only. std::string is not one. Fix: Use if/else if for string dispatch, or switch on a single char.


Bug: A switch will not compile and names two of your cases.

error: duplicate case value
        case 'A':
        ^~~~
note: previously used here

Means: Two case labels share a value. C++ would not know where to jump. Fix: Renumber or delete the duplicate. If you meant two labels to share one body, stack them with no code between — that is fall-through, not duplication.


Bug: A switch that built yesterday breaks when you declare a variable inside a case.

error: jump to case label
        case 2:
             ^
note:   crosses initialization of 'int total'

Means: All case labels share one scope. Jumping past a declaration would leave the variable undefined, so C++ forbids it. Fix: Wrap that case’s body in its own braces — case 1: { int total = 5; ... break; } — or declare the variable above the switch.


Bug: An else will not compile.

error: 'else' without a previous 'if'
    else {
    ^~~~

Means: Something came between the if block and the else — a stray statement, a stray semicolon, or a brace in the wrong place. Fix: Look at the lines immediately above the else. It must attach directly to the end of its if block.


Bug: You use the loop counter after the loop and the compiler says it does not exist.

error: 'i' was not declared in this scope
    cout << "last i was " << i << endl;
                             ^

Means: Scope. A variable declared in a for header lives only inside that loop. The same is true of anything declared inside a loop body — which is the usual cause of “my accumulator resets every pass.” Fix: Declare it before the loop: int i = 0; for (i = 0; i < 5; i++) { ... }.


Bug: A loop “does nothing,” or a block runs exactly once regardless.

warning: this 'for' clause does not guard... [-Wmisleading-indentation]
    for (int i = 1; i <= 5; i++);
    ^~~
note: ...this statement, but the latter is misleadingly indented as if it were guarded by the 'for'

Means: A stray semicolon right after for (...), while (...), or if (...). That semicolon is the whole body — an empty statement. while (i < 3); is also a guaranteed infinite loop, since the body that would change i is not part of the loop. Fix: Delete the semicolon, then check the rest of the file for the same slip.


Bug: A line you indented under an if runs every single time.

warning: this 'if' clause does not guard... [-Wmisleading-indentation]
    if (age >= 18)
    ^~
note: ...this statement, but the latter is misleadingly indented as if it were guarded by the 'if'

Means: No braces. An if without braces controls exactly one statement; the compiler does not read indentation. Fix: Add braces. Then add them everywhere else (§2.4).


Bug: Comparing two pieces of text gives a nonsense answer.

warning: comparison between two arrays [-Warray-compare]
    cout << boolalpha << ("apple" < "banana") << endl;
                          ~~~~~~~~^~~~~~~~~~

Means: You compared bare string literals, which compares memory addresses, not letters. Fix: Put them in std::string variables first, then compare (§2.2).


Bug: A variable you meant to use is being ignored.

warning: unused variable 'count' [-Wunused-variable]
    int count = 0;
        ^~~~~

Means: You declared it and never read it. In Week 2 that nearly always means you declared a counter and forgot to increment it, or forgot to print it. Fix: Use it or delete it. Do not silence it — this warning is usually reporting a missing line of logic.


Bug: A wall of errors ending in something about the end of the file.

error: expected '}' at end of input
note: to match this '{'
int main() {
           ^

Means: A missing closing brace. You opened a loop, if, or switch and never closed it, so the compiler ran out of file while still inside main. Fix: Re-indent the whole file — OnlineGDB has a formatter. The line where indentation stops making sense is where the brace belongs.


Bug: A break outside a loop.

error: break statement not within loop or switch
        break;
        ^~~~~

Means: break only means something inside a loop or a switch. You put one in a bare if, probably meaning “stop this branch.” Fix: There is no “stop this if.” Restructure with else, or — from Chapter 3 — return out of a function.


Bug: A case label that uses a variable.

error: the value of 'n' is not usable in a constant expression
        case n:
             ^
note: 'int n' is not const

Means: case labels must be known at compile time. A regular variable is not. Fix: Use a literal, a const, or go back to if/else if, which happily compares against variables.


Bug: A switch runs two cases when you expected one. Says: Possibly nothing at all — this can compile completely clean. Means: A missing break;. Execution fell through into the next case and kept going. Fix: Add the break;. When fall-through is what you want, stack the labels with no code between and comment it. Assume the compiler will not save you here.


Bug: The program hangs. No output, no crash, no prompt. Says: Nothing — it compiled fine. This is a runtime problem. Means: An infinite loop. Either the loop variable never changes (§2.16), or cin failed and the sentinel can never match (§2.12 sidebar). Fix: Stop it, add a trace print at the top of the loop body, run again. If the traced variable never moves, you found it. If nothing prints at all, the loop is stuck on a broken cin; guard the read with if (!(cin >> x)) { break; }.


Bug: An average comes out as 0, or the program dies right where the average is computed. Says: Nothing at compile time. Means: You divided by a counter that was zero, because the loop never ran. Integer division by zero is undefined behavior — on Linux runners like OnlineGDB’s it typically terminates the program; elsewhere it may hand you a nonsense number. Fix: Guard it, and cast for the fraction:

if (count > 0) {
    double average = static_cast<double>(total) / count;
    cout << "Average: " << average << endl;
} else {
    cout << "No entries." << endl;
}

Without static_cast<double> you get integer division and lose the fraction — the Chapter 1 trap, showing up inside a loop.


Coach’s Note — Start a file called my_bugs.txt this week. Every time an error costs you more than five minutes, paste the message and the one-line fix. By Week 8 it will be a better debugging reference than any textbook, because it is indexed by the mistakes you actually make.


2.19 — Reps

The full set is in the exercises, and every rep there ships with the exact output you should see, so you can grade yourself. Three teasers:

Teaser 1. Ask for a number 1–7 and print the matching day of the week. Write it with if/else if/else, then rewrite it with switch. Notice which one you would rather maintain.

Teaser 2. Print 1 through 20 on one line, space-separated, with a for loop. Then again counting down from 20. Then a third time printing only the even ones — with an if inside the loop, not by changing the step.

Teaser 3. Combine the halves: ask for a number until the user types one between 1 and 10, rejecting everything else with a message, counting attempts, and printing Took N attempt(s). when they comply. That is a loop, a conditional, a counter, and input validation in about fifteen lines — the exact skeleton the project needs.

There are more reps this week than in a normal chapter, because this is a double-width week. That is the deal you signed up for.


2.20 — Checkpoint: Can You Do This Yet?

Close the book, close the tabs, open a blank OnlineGDB file. Write each of these from memory — no copying, no autocomplete, no AI. Compile and run each one.

  1. An if/else if/else cascade with four branches converting a 0–100 score into a letter grade, correctly ordered.
  2. A condition true only when age is at least 18 and verified is true; and a second true when either score is at least 90 or extra_credit is true. Parenthesized so a stranger could read them.
  3. A switch on a char with three real cases, one pair of stacked labels sharing a body, and a default. Every case terminated correctly.
  4. A while loop that reads integers until the user types -1, accumulates the total, counts the entries, and prints the average — guarded so it neither divides by zero nor hangs when input runs out.
  5. A for loop that prints exactly ten iterations, written both ways: starting at 0, and starting at 1.
  6. A nested loop printing a triangle of stars five rows tall, where row n has n stars.
  7. Write down, without running it, what this prints — then run it and check:
    int x = 5;
    if (x = 3) {
        cout << "A" << endl;
    } else {
        cout << "B" << endl;
    }
    cout << x << endl;

Pass bar: 6 of 7, first try, without looking anything up. Item 7 is the tiebreaker — if you cannot explain the answer, §2.8 has not landed, and that bug will cost you an evening later.

If you scored 5 or below, do not start the project. Items 1–3 send you back to §2.4–§2.8; items 4–6 to §2.11–§2.14. Re-do those reps, then take this checkpoint again with different numbers. This is not a formality: the project is a five-hour job for someone with these seven things in their fingers and a twelve-hour job for someone without them. An extra hour here is the cheapest trade available to you this week.


2.21 — When You’re Stuck (and Nobody’s in the Room)

It is Tuesday at 11 p.m., your loop prints nothing, and there is nobody to ask. Work the ladder in order. Most Week 2 problems die on rung 2.

Rung 1 — Read the first error, out loud. Not the last one, not the scariest one. Scroll to the top of the compiler output, read the first error: line aloud, and find it in §2.18. Nearly every error you will hit this week is catalogued there with the real text.

Rung 2 — Print the loop variable. Or the condition. This is the highest-yield ten seconds in the chapter, and it is week-specific for a reason: almost every Week 2 mystery is “I do not actually know what this variable is doing.” So look:

cout << "[debug] top of loop: input=" << input << " total=" << total
     << " count=" << count << endl;

For a conditional that fires wrongly, print the condition itself before the if:

cout << boolalpha << "[debug] (warmth >= 5) is " << (warmth >= 5) << endl;

Nine times in ten the bug is now on screen. Delete the debug lines afterward.

Rung 3 — Cut it down to the smallest program that still misbehaves. Copy your file into a new OnlineGDB tab and start deleting: the dialog text, then branches, until it is under twenty lines. Either the bug disappears — and whatever you deleted last is the culprit — or you are holding a tiny program you can read in one pass. Never debug 200 lines when 15 have the same bug.

Rung 4 — Trace it on paper. Actually on paper. One column per variable, one row per iteration, filled in by hand, playing compiler. Slow — and the only technique that reliably catches off-by-one errors and mis-ordered cascades, because it forces you to evaluate the condition instead of assuming what it means. Three iterations is usually enough. The first row where your trace disagrees with the real output is your bug.

Rung 5 — Explain the loop out loud, one line at a time. To a roommate, a pet, an empty chair. The rubber duck works because your mouth cannot skip a step your eyes will. Say what each line does, not what you meant. Half the time you interrupt yourself.

Rung 6 — Re-read the one section that covers it. Symptom to section, so you do not have to hunt:

SymptomGo to
Wrong branch fires§2.4 (cascade order), §2.8 (= vs ==)
A combined condition behaves oddly§2.5 (short-circuit, parentheses)
switch misbehaves or will not build§2.7 and the switch entries in §2.18
Two doubles that look equal compare unequal§2.3
Program hangs, prints nothing§2.12 sidebar, §2.16
Loop runs one too many or one too few times§2.15
Total or average is wrong§2.13 (accumulator rules)
Ending depends only on the last choice§2.17

Rung 7 — Post to the discussion board, specifically. Other students are stuck on this same week right now; a good post gets answered fast, a vague one gets ignored. Post this shape:

Title: Week 2 — sentinel loop never exits when I type -1

Trying to do: read integers until the user types -1, then print the total. What happens instead: hangs and prints nothing after the first entry. Smallest version that still breaks: (paste the 12 lines, not the whole file) Already tried: printed input at the top of the loop — it prints 0 every pass, forever. Read the §2.12 sidebar on a failed cin and I think that is what this is, but I do not see what my loop should do about it.

A specific symptom, a minimal reproduction, evidence you climbed the ladder. Answer someone else’s post while you are there — explaining a bug you already fixed is worth about three reps.

Rung 8 — Email the instructor, using this template so you get a useful answer in one round trip instead of three:

Subject: Coding 1 (Accelerated) — Week 2 — nested loop prints wrong row count

Section: §2.14, nested loops. Building: Project 2, Normal tier. Expected: five rows, row n has n stars. Actual: five rows of five stars. Minimal code that compiles:

for (int row = 1; row <= 5; row++) {
    for (int col = 1; col <= 5; col++) { cout << "* "; }
    cout << endl;
}

Ladder steps done: no compiler error; traced row and col; cut to 6 lines; traced two iterations on paper; re-read §2.14. Best guess: the inner bound should depend on row, but I am not sure how to write that. OnlineGDB link: (paste it — set the compiler flags to -Wall -Wextra first)

Send the link, not a screenshot. A grader who can run your code answers in minutes; a grader squinting at a photo of a monitor answers in days.

One thing not on the ladder: do not paste your project into an AI and ask it to fix the loop. Not because it will not work — it will — but because this is the week your hands learn loops, and hands do not learn from watching. The Week 4 midterm is a code-reading exam that asks what a loop prints. There is no shortcut through that, and this is the week the shortcut is most tempting and most expensive.


2.22 — This Week’s Project

You are ready for P2 — The Coffee Shop Conversation, in Project 2, due at the end of Week 2.

The setup: a friend you have known for years — someone you respect, someone who respects you — sets their cup down and asks the hardest question in the deck. “If God is good, why is there so much suffering?” You can hear that it is not a debate move. Something happened; you do not know what yet. Your program models that conversation. It branches on what you say, it keeps going turn after turn, and it remembers.

One direct request, and it is a grading criterion, not a suggestion: this project does not produce “you converted your friend” endings. The endings you write are the real ones — the conversation went deeper, or you stumbled and were honest about it, or you said “I don’t know” and your friend respected it, or you got defensive and the moment closed, or something more pressing interrupted and that was okay. Programs that pretend hard conversations have easy wins are bad witnesses. We are not going to write one.

Both halves of this week are load-bearing. The conditionals give you the branches: nested ifs where the follow-up genuinely depends on the answer, boolean flags gating later moments on earlier ones, a switch where a decision has several clean options, combined conditions in the final dispatch. The loops give you the conversation itself: a scene that runs turn after turn instead of ending after one question, input validation that does not punish a typo, and an accumulator that makes the whole run matter rather than just the last choice. code/conversation_loop.cpp is the miniature; build yours bigger.

Two things that will save you hours. Write the endings first, in English, on paper — decide what outcomes are possible before deciding what code produces them, then work backwards to the conditions. Students who code first end up with four endings that are secretly the same ending. And start from code/coffee_shop_starter.cpp, which compiles clean and runs but does not meet Normal tier: the loop is there, the branches are stubbed, and the TODOs mark what you owe.

Read Project 2 for the required features, tiers, and rubric. Submission is an OnlineGDB link; the workflow is in Appendix A.


2.23 — Coach’s Final Word for Week 2

You just doubled the size of the language you can write.

Two weeks ago your programs computed one thing and stopped. Now they can decide, and they can persist, and — the part that matters — they can decide while persisting. Almost every program that exists is some arrangement of those two moves. A game loop is a loop full of conditionals. A search is a loop with an if in it. A menu is a loop wrapped around a switch. You own the machinery now; everything after this is refinement.

The compiler checks your syntax. It never checks your question. It will tell you when if (score = 100) looks suspicious. It will never tell you that you asked about the wrong variable, ordered your cascade backwards, or looped one time too many. That part is on you, permanently, and you get good at it by tracing loops on paper until you no longer need to.

And repetition is not rote. Nothing worth having is built in one pass — not a program, not a skill, not a practice, not a person. You do the reps; the reps do the forming. Your program this week will not make anyone practice anything. It will just count, honestly, while you do.

When you write Project 2, remember what the conversation is actually about. You are not building a debate flowchart. You are modeling how a hard question gets engaged in a real room by two people who care about each other. Take that seriously and the conditionals will earn their keep.

Next week: functions and collections. We stop repeating ourselves.


Up next: Work every rep in the exercises — each one states the exact output to expect, so you can grade yourself. Take the §2.20 Checkpoint honestly. Then build Project 2, The Coffee Shop Conversation. After that, Chapter 3 — functions and collections.

Check Your Reps

Week 2 Knowledge Check

Question 1 of 6
What does this print?
int x = 5;
if (x = 3) {
    cout << "yes " << x;
} else {
    cout << "no " << x;
}
Why: `=` assigns; `==` compares. This stores 3 in x, and 3 is non-zero so the condition is true. It compiles — g++ only warns `suggest parentheses around assignment used as truth value`. That is exactly why this course requires -Wall -Wextra.
Question 2 of 6
What does this loop print?
for (int i = 0; i < 5; i++) {
    if (i == 2) continue;
    if (i == 4) break;
    cout << i << " ";
}
Why: `continue` skips only the rest of that one pass, so 2 is not printed but the loop goes on. `break` leaves the loop entirely at 4, before printing. Confusing the two changes which items get processed.
Question 3 of 6
This program prints `0` eleven times and then stops. What is wrong with it?
int i = 0;
int guard = 0;
while (i < 5) {
    cout << i << " ";
    if (++guard > 10) break;
}
cout << "stopped";
Why: Nothing in the body changes `i`, so `i < 5` is true forever — only the guard counter stops it. Every while loop needs something in its body that moves it toward its exit condition.
Question 4 of 6
What does this print?
char g = 'B';
switch (g) {
    case 'A': cout << "top" << endl;
    case 'B': cout << "good" << endl;
    case 'C': cout << "okay" << endl;
              break;
    default:  cout << "other" << endl;
}
Why: Execution jumps to case 'B' and falls through into case 'C' because 'B' has no `break`. The break under 'C' finally stops it, so default never runs. g++ warns `this statement may fall through`.
Question 5 of 6
Why is `if (a == b)` unreliable when a and b are doubles?
Why: Values like 0.1 have no exact binary form, so arithmetic leaves tiny residues. Compare with a tolerance instead: `if (fabs(a - b) < 0.0001)`.
Question 6 of 6
Which expression is true only when x is between 10 and 20 inclusive?
Why: You must compare twice and join with &&. The maths-looking `10 <= x <= 20` compiles but is wrong — it evaluates `10 <= x` to 1 or 0 and then compares that to 20, so it is essentially always true. The `||` version is true for nearly every number.
YOU FINISHED. NICE WORK.