Chapter 01 · Reps

The Sport of Programming and the Memory It Runs On — Reps

← Back to Chapter 1

Chapter 1 — Reps

Conditioning, not grading. Nobody collects these and nobody checks. Do every one of them, in order, before you open Project 1.

Ground rules:

  1. Type every line yourself. No copy-paste. Your hands are part of the training.
  2. Compile and run after every rep. If you didn’t see output, you didn’t do the rep — you wrote code.
  3. When something fails, read the first error before you do anything else. Then fix it from memory.
  4. AI stays off for the reps. They’re short enough that you can afford to struggle, and the struggle is the training.

One thing that’s different in this edition. Every rep below ships with the exact output it produces. That block is not decoration — it’s your grader. Run your program, put your output next to the block, and compare them character by character. Same? Move on. Different? You have a bug, and you found it yourself at midnight with nobody in the room. That’s the whole point.

Where a rep depends on something only you know — your name, your city — the expected block shows a real transcript for one stated sample input, so the shape is still checkable even when the words are yours.

Put -Wall -Wextra in OnlineGDB’s compiler-flags box before you start (Appendix A). Reps 5, 7, 8 and 9 depend on it.


Reps 1–5: The Compile–Run Loop

Session 1 material — §1.1 through §1.11. These five are the whole loop: type, compile, run, compare, break.

Rep 1 — Hello, cold

Open a blank file. Type the six-line program from §1.6 by hand — #include, using namespace std;, int main(), one cout, return 0;, closing brace. Do not paste it. Compile, run.

Expected output:

Hello, world.

Now change only the string literal to something of your own and run again. Sample: the line Week 1 of 8. Let's get to work. produces exactly this.

Expected output:

Week 1 of 8. Let's get to work.

You’ve just run the loop end to end. You’ll run it several hundred more times.


Rep 2 — Three lines

Print three lines: your name, where you live, what you had for breakfast. One cout per line, each ending in endl.

Sample values Marcus / Milwaukee, Wisconsin / oatmeal give this.

Expected output:

My name is Marcus.
I live in Milwaukee, Wisconsin.
I had oatmeal for breakfast.

Check the shape, not the words: three lines, no blank line you didn’t ask for, no line running into the next.


Rep 3 — Take the endls out

Same program. Delete every endl. Compile, run. With the same sample values:

Expected output:

My name is Marcus.I live in Milwaukee, Wisconsin.I had oatmeal for breakfast.

That’s one line, and the output ends with no newline at all — so in OnlineGDB the ...Program finished with exit code 0 notice lands immediately after breakfast. rather than on its own line.

Put one endl back. Then all of them. cout prints exactly what you send it and nothing more (§1.8); every gap in your output is a gap you asked for.

Then, in the same file, add two more lines to practise the other escapes from §1.8 — \" for a literal double-quote inside a string, and \\ for a literal backslash. A program printing only those two lines produces:

Expected output:

The compiler said: "expected ';' before 'return'"
A backslash looks like this: \

If you got a missing terminating " character error here, you wrote " where you needed \". That’s Rep 5’s break 5, arriving early.


Rep 4 — Talk, listen, and the two ways to listen

Four parts, one after another. This is the rep behind Project 1’s card half, so do all four.

(a) Write the prompt-and-read pair from §1.9: a string, a prompt, cin >> name, then a greeting. Run it and type Marcus.

Expected output (the text after each prompt is what you typed):

What is your name? Marcus
Welcome to the gym, Marcus.

(b) Run the same program again. This time type Marcus Aurelius.

Expected output:

What is your name? Marcus Aurelius
Welcome to the gym, Marcus.

Aurelius is gone from the greeting — cin >> stops at the first whitespace. It isn’t lost, it’s waiting in the buffer for the next read.

(c) Change cin >> name; to getline(cin, name);. Nothing else. Run it and type Marcus Aurelius again.

Expected output:

What is your name? Marcus Aurelius
Welcome to the gym, Marcus Aurelius.

(d) The break. New program: read an int with cin >>, then read a name with getline, then echo both inside square brackets so you can see emptiness. Leave cin.ignore() out on purpose. Run it, type 18, then try to type Marcus Aurelius.

Expected output:

How many reps? 18
Your name: ---
reps = [18]
name = []

You never got to type the name. The program blew past the prompt and name came back empty, because cin >> reps left your Enter keypress sitting in the buffer and getline read up to it — nothing. Now add cin.ignore(); between the two reads and run it again:

Expected output:

How many reps? 18
Your name: Marcus Aurelius
---
reps = [18]
name = [Marcus Aurelius]

One line of code, and the difference is a program that works versus a program that appears to skip an input. §1.24’s Checkpoint calls this item non-negotiable. It’s why.


Rep 5 — The error catalog (bug drill)

Take your Rep 1 file. Make each change below one at a time, compile, read the message, then put it back before the next one. You are not memorizing text — you are learning that compiler messages are readable.

The output below is real GNU g++ 14.2.0 output, the same compiler family OnlineGDB runs, captured from exactly these broken files. Your line and column numbers will match if your file matches §1.6’s; the wording can shift slightly between compiler versions. Where a message runs long, only the lines that carry the meaning are reproduced — on screen you’ll also get the source line quoted back with a caret under the exact spot. Read that too; it’s signal, not noise.

1. Delete the semicolon after endl.

break1.cpp: In function 'int main()':
break1.cpp:5:36: error: expected ';' before 'return'
    5 |     cout << "Hello, world." << endl
      |                                    ^
      |                                    ;
    6 |     return 0;
      |     ~~~~~~

Blamed on line 5, noticed on line 6 — and g++ prints the ; it wants. When an error names two lines, look at the first.

2. Delete #include <iostream>.

break2.cpp:4:5: error: 'cout' was not declared in this scope
break2.cpp:1:1: note: 'std::cout' is defined in header '<iostream>'; this is probably fixable by adding '#include <iostream>'
break2.cpp:4:32: error: 'endl' was not declared in this scope
break2.cpp:1:1: note: 'std::endl' is defined in header '<ostream>'; this is probably fixable by adding '#include <ostream>'

Two errors from one deletion, and both note: lines hand you the fix.

3. Put the include back; delete using namespace std; instead.

break3.cpp:4:5: error: 'cout' was not declared in this scope; did you mean 'std::cout'?
break3.cpp:4:32: error: 'endl' was not declared in this scope; did you mean 'std::endl'?

Same message as break 2, different tail. did you mean 'std::cout'? means the name exists but you didn’t open its namespace.

4. Change int main() to int Main(). This one comes from the linker, not the compiler, so its wording depends on your platform. The line you’ll see either way:

collect2: error: ld returned 1 exit status

On OnlineGDB (Linux) it’s preceded by undefined reference to 'main'. On a Mac the same failure reads Undefined symbols for architecture arm64: / "_main", referenced from: — that’s the phrasing captured here. Identical complaint, different accent, and §1.11 covers both. When a message mentions ld or collect2, you’re past the compiler: your code was fine, but nothing named main existed to start the program.

5. Delete the closing quote on the string.

break5.cpp:5:13: warning: missing terminating " character
break5.cpp:5:13: error: missing terminating " character
break5.cpp: In function 'int main()':
break5.cpp:6:5: error: expected primary-expression before 'return'

That third message is an aftershock — nonsense produced by a compiler still confused from the first. Fix the first error, recompile, watch the rest vanish. Never try to fix them all.

6. Put everything back, then add int reps = 18; at the top of main and never use it.

break6.cpp:5:9: warning: unused variable 'reps' [-Wunused-variable]
    5 |     int reps = 18;
      |         ^~~~

A warning, not an error — the program still builds and runs correctly. It also costs you the “compiles with no warnings” line on every rubric in this book. Delete the variable.

Six messages you have now seen with your own eyes. The next time one shows up in a project it costs you thirty seconds instead of thirty minutes.


Reps 6–11: The Boxes and the Trap

Session 2 material — §1.12 through §1.17. Memory, and the one bug that will silently wreck your project.

Rep 6 — One of each

Declare one variable of each of the five types from §1.13 and print all five with labels. Send cout << boolalpha; before you print. Use these exact values so you can check yourself: 2026, 12.0, false, 'G', "1 Peter 3:15".

Expected output:

int:    2026
double: 12
bool:   false
char:   G
string: 1 Peter 3:15

Two things to stare at. The double was initialized to 12.0 and printed 12, because C++ drops trailing zeros by default — that’s §1.18’s whole reason for existing. And now delete the boolalpha line and run it again:

Expected output:

int:    2026
double: 12
bool:   0
char:   G
string: 1 Peter 3:15

false became 0. Put the line back. Project 1 requires true/false.


Rep 7 — char vs string (bug drill)

Declare char category = 'G'; and string category_name = "gravitational";. Print both with labels.

Expected output:

char   category:      G
string category_name: gravitational

Now break it: change the char line to char category = "G"; — double quotes instead of single.

Expected output:

rep07_broken.cpp: In function 'int main()':
rep07_broken.cpp:7:21: error: invalid conversion from 'const char*' to 'char' [-fpermissive]
    7 |     char category = "G";
      |                     ^~~
      |                     |
      |                     const char*

'G' is one character; "G" is a two-byte string. On the page they’re nearly identical. To the compiler they’re unrelated. Fix it and confirm you’re back to the first output.


Rep 8 — Declare, assign, and hit a wall (bug drill)

Start int score = 0; and print it. Then walk it through = 10, = score + 5, += 5, -= 6, *= 2, printing after each step. Then add const int WEEKS_IN_COURSE = 8; and const double HOURS_PER_WEEK = 12.0;, and print both plus their product.

Expected output:

start:          0
after = 10:     10
after + 5:      15
after += 5:     20
after -= 6:     14
after *= 2:     28
weeks:          8
hours/week:     12
total hours:    96

Ninety-six hours. That’s this course, and it isn’t a metaphor.

Now break it. Add WEEKS_IN_COURSE = 16; after the declaration.

Expected output:

rep08_broken.cpp: In function 'int main()':
rep08_broken.cpp:7:21: error: assignment of read-only variable 'WEEKS_IN_COURSE'
    7 |     WEEKS_IN_COURSE = 16;
      |     ~~~~~~~~~~~~~~~~^~~~

Refusing is the entire purpose of const (§1.14). The compiler caught your mistake at build time, when it was free.


Rep 9 — The five operators

Read two ints from the user and print all five arithmetic results, one per line. Test with 7 and 2:

Expected output:

a: 7
b: 2
a + b = 9
a - b = 5
a * b = 14
a / b = 3
a % b = 1

Run it again with 15 and 4:

Expected output:

a: 15
b: 4
a + b = 19
a - b = 11
a * b = 60
a / b = 3
a % b = 3

15 / 4 is 3, not 3.75. Note that too — it’s the same trap twice, and Rep 10 is about to make you predict it.

Now break it: change a to a double, give it 7.5, and try a % b.

Expected output:

rep09_broken.cpp: In function 'int main()':
rep09_broken.cpp:8:15: error: invalid operands of types 'double' and 'int' to binary 'operator%'
    8 |     cout << a % b << endl;
      |             ~ ^ ~
      |             |   |
      |             |   int
      |             double

Modulo is integers only (§1.15).


Rep 10 — Trace it by hand first

Do not type this in yet. Read it and write down all seven values on paper — actual paper, or a comment block. Only then create the file, compile, and compare. This is the single highest-yield drill for the Part A code-reading exam, and it only works if you commit to an answer before you run it.

#include <iostream>
using namespace std;

int main() {
    int constants_in_range = 7;
    int constants_examined = 9;

    double fraction = constants_in_range / constants_examined;
    double percent  = fraction * 100.0;

    int total_hours = 96;
    int weeks = 8;
    int per_week = total_hours / weeks;
    int leftover = total_hours % weeks;

    int a = 7;
    int b = 2;

    cout << "fraction: " << fraction << endl;
    cout << "percent:  " << percent << endl;
    cout << "per_week: " << per_week << endl;
    cout << "leftover: " << leftover << endl;
    cout << "a / b:    " << a / b << endl;
    cout << "a / 2.0:  " << a / 2.0 << endl;
    cout << "b / a:    " << b / a << endl;
    return 0;
}

Expected output:

fraction: 0
percent:  0
per_week: 12
leftover: 0
a / b:    3
a / 2.0:  3.5
b / a:    0

Score yourself honestly. Most people miss fraction and percent — seven ninths is about 0.78, but 7 / 9 is evaluated under integer rules before anything is stored in a double, so the fraction is destroyed one step earlier and percent inherits a zero. Declaring the destination double does not reach back and fix the division (§1.16).

If you missed b / a, same rule: 2 / 7 truncates to 0. If you missed a / 2.0, notice that one operand is already a double, so no truncation happens at all.


Rep 11 — Four ways out, and one way in

Fix the trap four ways in one program, all with a = 7 and b = 2: a C-style cast on the dividend, static_cast<double>, a cast on the divisor, and multiplying by 1.0. Print the un-fixed double trap = a / b; alongside them. Then add two deliberate truncations with static_cast<int>: one on 9.99, one on -3.7.

Expected output:

a = 7, b = 2
double trap = a / b:              3
(double)a / b:                    3.5
static_cast<double>(a) / b:       3.5
a / (double)b:                    3.5
a * 1.0 / b:                      3.5
static_cast<int>(9.99):           9
static_cast<int>(-3.7):           -3

Four different spellings, one answer. Pick static_cast<double> for real code — it announces in the source that a conversion is happening on purpose, and it’s ugly enough to notice.

The two truncations are the real lesson: 9.99 becomes 9, not 10, and -3.7 becomes -3, not -4. Conversion toward int truncates, it does not round (§1.17). Write static_cast<int> when you mean it, so the next reader can see you meant it.


Reps 12–15: Shaping the Output

Session 3 material — §1.18 and §1.19. Numbers a human can read.

Rep 12 — What setprecision alone actually does

Compute margin = (5.9e-39 - 1.0e-40) / (1.0e-38 - 1.0e-40) with const bounds, and set gift = 1234.5. Print margin, gift, and 5.9e-39 at the default settings. Then send setprecision(3) — with no fixed — and print margin and gift again.

Expected output:

default (6 significant digits):
  margin = 0.585859
  gift   = 1234.5
  tiny   = 5.9e-39

setprecision(3) alone = 3 SIGNIFICANT digits:
  margin = 0.586
  gift   = 1.23e+03

1234.5 became 1.23e+03. Three significant digits isn’t enough to write that number plainly, so the stream gave up and switched to scientific notation. If you were formatting money, you just shipped a bug.


Rep 13 — The money recipe, and where it fails

Same values. This time send fixed and setprecision(2) together, print all three, then switch to setprecision(4) and print margin once more.

Expected output:

fixed + setprecision(2) = 2 digits AFTER the point:
  margin = 0.59
  gift   = $1234.50
  tiny   = 0.00

same stream, setprecision(4):
  margin = 0.5859

Three things worth keeping. $1234.50 — trailing zero included — is the money recipe. fixed is wrong for tiny magnitudes: 5.9e-39 printed as 0.00, which erases the value rather than rounding it. And you never re-sent fixed; it’s sticky and stayed on for everything after it.

That middle lesson is exactly why Project 1’s report formats its ratios with setprecision but leaves the physical values in their default scientific form.


Rep 14 — setw beats \t

Print three name/number pairs separated by "\t", using the names Marcus, Maya, and Bartholomew. Then print the same three as a table using setw(12) and setw(6) with | separators. You’ll need #include <iomanip>.

Expected output:

With tabs:
Marcus	12
Maya	9
Bartholomew	7

With setw:
|        Name|  Reps|
|      Marcus|    12|
|        Maya|     9|
| Bartholomew|     7|

Look at the tab section: Marcus and Maya line their numbers up, and Bartholomew doesn’t, because a tab jumps to the next fixed stop rather than to the column you had in mind. setw aligns to a width you chose and works no matter how long the text is. Remember it applies to the next item only — that’s why every column needs its own.

Then delete the #include <iomanip> line and compile, so you’ve seen this once on purpose:

Expected output:

rep14_broken.cpp:14:20: error: 'setw' was not declared in this scope
rep14_broken.cpp:3:1: note: 'std::setw' is defined in header '<iomanip>'; this is probably fixable by adding '#include <iomanip>'

Read the note: — g++ names the header. fixed and boolalpha live in <iostream>; setw and setprecision don’t, which is why this error shows up exactly when you start prettying up your output.


Rep 15 — Choosing a value without if

You don’t have if until Chapter 2, so use §1.19’s trick. Set char category = 'G'; and const int BASE_POINTS = 10;. Build gravity_bonus as (category == 'G') * 5 and cosmological_bonus as (category == 'C') * 3, total them, and print everything with boolalpha on.

Expected output:

category:            G
(category == 'G'):   true
(category == 'C'):   false
gravity_bonus:       5
cosmological_bonus:  0
total:               15

Now change the initializer to 'C' and rerun:

Expected output:

category:            C
(category == 'G'):   false
(category == 'C'):   true
gravity_bonus:       0
cosmological_bonus:  3
total:               13

Exactly one comparison is true each time, so exactly one bonus is non-zero. A comparison is a value, and in arithmetic true is 1. It’s a one-week stopgap and if will replace it next week — but feel it once, because arithmetic and logic being the same thing underneath is not a beginner’s fact.


Reps 16–18: Putting It Together

Rehearsal for both halves of Project 1. Build these and the project becomes assembly rather than invention.

Rep 16 — The card half

Read a rep count with cin >>, then a name and a hometown with getline (you will need cin.ignore() — Rep 4d), then print a bordered card in the style of §1.8.

Sample input 18 / Marcus Aurelius / Milwaukee, Wisconsin:

Expected output:

How many reps have you finished? 18
Your name: Marcus Aurelius
Where are you from? Milwaukee, Wisconsin

+-----------------------------------+
|           APOLOGIST CARD          |
+-----------------------------------+
  Name:  Marcus Aurelius
  From:  Milwaukee, Wisconsin
  Week:  1 of 8
  Reps:  18 of 18
+-----------------------------------+

If the Name: row is blank, your cin.ignore() is missing. If it says Marcus and the From: row says Aurelius, you used cin >> where you needed getline. Both are §1.10.


Rep 17 — The report half

Read a constant name with getline, then a measured value (double) and two counts (int) with cin >>. Hard-code const double LOW_BOUND = 1.0e-40; and const double HIGH_BOUND = 1.0e-38;. Compute the range width, the margin, a bool for whether the value falls inside the bounds, and the in-range fraction using static_cast<double>. Print with boolalpha on, and send setprecision(4) immediately before the margin line so the range width still prints at default.

Sample input gravitational coupling / 5.9e-39 / 7 / 9:

Expected output:

Constant name: gravitational coupling
Measured value: 5.9e-39
Constants in the life-permitting range: 7
Constants examined: 9

+------------------------------------------------+
|               FINE-TUNING REPORT               |
+------------------------------------------------+
  Constant:            gravitational coupling
  Measured value:      5.9e-39
  Life range:          [1e-40, 1e-38]
  Range width:         9.9e-39
  Margin in range:     0.5859
  Life-permitting:     true
  Constants in range:  7 of 9
  Fraction:            0.7778
+------------------------------------------------+

Note 1.0e-40 printing as 1e-40. That’s display, not value. And margin came out right with no cast anywhere, because every operand in it was already a double — the trap fires only when both operands are integers.


Rep 18 — Break the finished thing (bug drill)

Take Rep 17. Delete static_cast<double> from the fraction line, leaving constants_in_range / constants_examined. Change nothing else. Compile — it builds clean, zero warnings — and run it on the same input.

Expected output:

Constant name: gravitational coupling
Measured value: 5.9e-39
Constants in the life-permitting range: 7
Constants examined: 9

+------------------------------------------------+
|               FINE-TUNING REPORT               |
+------------------------------------------------+
  Constant:            gravitational coupling
  Measured value:      5.9e-39
  Life range:          [1e-40, 1e-38]
  Range width:         9.9e-39
  Margin in range:     0.5859
  Life-permitting:     true
  Constants in range:  7 of 9
  Fraction:            0
+------------------------------------------------+

One line differs. The program compiles, runs, and prints a confident, well-formatted, wrong answer — with no warning from anything.

That’s the bug you’re actually being trained against this week. A crash tells you where it is. This doesn’t. The only defense is testing with numbers whose answer you already know, which is why every expected-output block above exists. Put the cast back.


Done? One Last Thing.

Open a blank file, from_memory_1.cpp. No notes, no prior file, no autocomplete, no AI. Write, from nothing:

  1. A read of a rep count into an int with cin >>.
  2. A cin.ignore().
  3. A read of a full name — spaces included — into a string with getline.
  4. A const int WEEKS = 8; and a double holding reps-per-week, computed so the fraction survives.
  5. A print of the name, the reps, and the per-week figure at exactly two digits after the decimal point.

Compile it. Run it with 18 and Marcus Aurelius.

Expected output:

Reps finished: 18
Your name: Marcus Aurelius
---
Marcus Aurelius finished 18 reps in 8 weeks.
Per week: 2.25

Three ways to read your own result:

  • Per week: 2.25 — you have the move. Open the project.
  • Per week: 2.00 — the integer-division trap got you. 18 / 8 is 2, and the .25 died before your double ever saw it. Re-drill Reps 10 and 11.
  • The name line is empty — you left out cin.ignore(). Re-drill Rep 4d.

If it didn’t work the first time, fix the bug, then write the whole thing again from scratch in another new file. Repeat until you can write it cold. Then take §1.24’s Checkpoint.

This last drill is the most important rep in the chapter. Nobody will know whether you did it. That’s exactly why it counts.


Up next: Project 1P1: Apologist’s Card & Fine-Tuning Report, due at the end of Week 1. Read the rubric before you write a line of code.