Chapter 04 · Reps

Grouping Memory: Structs — and the Midterm — Reps

← Back to Chapter 4

Chapter 4 — Reps

Conditioning, not grading. Type every line, compile it, run it. AI off.

Every rep states the exact output you should see. That is your grader — nobody is looking over your shoulder this week. Match it character for character and you have the move; if you do not, the difference tells you where to look. Every output block here was produced by compiling a solution with g++ -std=c++17 -Wall -Wextra and running it. Nothing is predicted; where a value is genuinely machine-dependent, the rep says so and says what is checkable.

Reps 1–14 drill this week. Reps 15–18 are cumulative review over Weeks 1–4, and each says which part of the midterm it prepares.


Reps 1–5: The Struct Itself

Rep 1 — Point, three ways

Declare struct Point { int x; int y; };. Build three points using the three forms from §4.2 — brace initialization for a (3, 4), declare-then-assign for b (-2, 7), and Point c{}; then c.x = 10 with c.y left alone. Print each as name = (x, y).

Expected output:

a = (3, 4)
b = (-2, 7)
c = (10, 0)

That 0 is not luck. It is the {} doing its job.


Rep 2 — Manuscript and the dot operator

Declare struct Manuscript { string name; int century; string type; };. Make an array of 3 filled with one brace list — P52 (2, papyrus), Codex Sinaiticus (4, uncial), Bodmer Papyri (3, papyrus) — and print each as name (century N, type).

Below the loop, print a blank line and three probes proving a struct field is an ordinary variable: ms[1].name.length(), ms[1].name.substr(0, 5), and — after cout << boolalpha; — whether ms[0].type == "papyrus".

Expected output:

P52 (century 2, papyrus)
Codex Sinaiticus (century 4, uncial)
Bodmer Papyri (century 3, papyrus)

ms[1].name.length() = 16
ms[1].name.substr(0, 5) = Codex
ms[0].type == "papyrus" is true

Rep 3 — print_manuscript, and what const& forbids

Refactor Rep 2: move the printing into void print_manuscript(const Manuscript& m) and make the loop body one call. Same records, same format, no probe lines.

Expected output:

P52 (century 2, papyrus)
Codex Sinaiticus (century 4, uncial)
Bodmer Papyri (century 3, papyrus)

Now break it. Inside print_manuscript, add m.century = m.century + 1; and recompile. This is §4.13’s bug 7. Here is the exact text g++ printed on the machine this book was built on (Apple clang 21):

bug_f.cpp:12:15: error: cannot assign to variable 'm' with const-qualified type 'const Manuscript &'
   12 |     m.century = m.century + 1;
      |     ~~~~~~~~~ ^
bug_f.cpp:11:41: note: variable 'm' declared const here
   11 | void print_manuscript(const Manuscript& m) {
      |                       ~~~~~~~~~~~~~~~~~~^
1 error generated.

GCC — what OnlineGDB runs — words it error: assignment of member 'Manuscript::century' in read-only object (§4.13). Different sentence, same event: you promised const and the compiler held you to it. Take the line back out.


Rep 4 — What {} actually buys you

Part (a). Declare Manuscript fragment{};, assign only fragment.name = "(unidentified fragment)";, then print the name, the century, the type inside square brackets, and fragment.type.length().

Expected output:

name:    (unidentified fragment)
century: 0
type:    []
type.length(): 0

Part (b). Same thing without the braces. In a fresh main: declare Manuscript bare; (no {}), set only bare.name, print bare.century. Then declare Manuscript safe{};, set only safe.name, and print safe.century and safe.type.empty() under boolalpha. It compiles clean, no warning. Run it three times.

All three runs printed this here, identically:

bare.century    = -147409056
safe.century    = 0
safe.type empty = true

Your bare.century will differ, and may differ between your own runs. That value is undefined, and undefined is not a synonym for random — here it looked perfectly stable, which is the most dangerous of the outcomes §4.13 bug 12 describes, because stable-looking garbage is garbage you will trust. What is checkable is the last two lines: 0 and true, every machine, every run. Two characters bought you that.


Rep 5 — Bug drill: four struct errors, on purpose

Start from your Rep 2 file. Make each change one at a time, compile, read the message, undo it. An error you caused yourself takes seconds to recognize later; one you only read about takes twenty minutes.

Below is what g++ printed here. Wording varies by compiler, so GCC’s phrasing is given alongside; §4.13 has its full text.

(1) Delete the ; after the struct’s closing brace.

bug_a.cpp:9:2: error: expected ';' after struct
    9 | }
      |  ^
      |  ;
1 error generated.

GCC: expected ';' after struct definition. §4.13 bug 1.

(2) Misspell a field: m.centruy.

bug_b.cpp:13:15: error: no member named 'centruy' in 'Manuscript'
   13 |     cout << m.centruy << endl;
      |             ~ ^
1 error generated.

Search phrase: no member named. GCC adds did you mean 'century'?. §4.13 bug 2.

(3) Print the struct directly: cout << m << endl;.

bug_c.cpp:13:10: error: invalid operands to binary expression ('ostream' (aka 'basic_ostream<char>') and 'Manuscript')
   13 |     cout << m << endl;
      |     ~~~~ ^  ~

…followed by 111 more lines of note: candidate function template not viable: from inside the standard library — 115 lines for one mistake. Ignore every note. GCC’s first line reads no match for 'operator<<' and buries you just as deep. Read line 1, fix line 1 (§4.13 bug 6).

(4) Add a fourth value: Manuscript m = {"P52", 2, "papyrus", 125};.

bug_d.cpp:12:42: error: excess elements in struct initializer
   12 |     Manuscript m = {"P52", 2, "papyrus", 125};
      |                                          ^~~
1 error generated.

GCC: too many initializers for 'Manuscript'. §4.13 bug 3.

Bonus — the one that is only a warning. Try too few: Manuscript m = {"P52", 2};, printing all three fields.

bug_e.cpp:12:29: warning: missing field 'type' initializer [-Wmissing-field-initializers]
   12 |     Manuscript m = {"P52", 2};
      |                             ^
1 warning generated.

It builds, and it runs:

P52 / 2 / []

Search the tag, not the sentence — -Wmissing-field-initializers is identical in both compilers. In this course a warning is a failure. Fill every field.


Reps 6–9: Arrays of Structs

These four build on each other. Keep the same five records throughout, in this order: P52 (2, papyrus), Codex Sinaiticus (4, uncial), Codex Vaticanus (4, uncial), Codex Bezae (5, uncial), Bodmer Papyri (3, papyrus).

Rep 6 — Five manuscripts, one array

One array of 5, one int count = N. Print a header Inventory (count = 5):, then each record indented two spaces as i: name (century N, type).

Expected output:

Inventory (count = 5):
  0: P52 (century 2, papyrus)
  1: Codex Sinaiticus (century 4, uncial)
  2: Codex Vaticanus (century 4, uncial)
  3: Codex Bezae (century 5, uncial)
  4: Bodmer Papyri (century 3, papyrus)

Say out loud what you did not write: three arrays, and three assignments per record.


Rep 7 — Scan it twice

Two passes, both from §4.3. First, find the earliest by tracking a running Manuscript oldest, printed through print_manuscript. Second, accumulate the centuries into an int total and print the total, the average with static_cast<double>, and the average without the cast.

Expected output:

Oldest: P52 (century 2, papyrus)
Century total: 18
Average century (cast):    3.6
Average century (no cast): 3

Same data, same divisor, one keystroke apart, and the second is wrong. Week 1’s integer-division trap is still the trap in Week 4 — which is why §4.18’s checklist puts it at number one.


Rep 8 — find_index and the −1 convention

Write int find_index(const Manuscript ms[], int count, const string& target) returning the first name match or -1. Call it three times, printing each result, then use a fourth call inside an if (i == -1) … else … that prints the found record.

Expected output:

find_index("Codex Vaticanus")    = 2
find_index("P52")                = 0
find_index("Codex Alexandrinus") = -1
Found at 3: Codex Bezae (century 5)

Note the parameter list: const Manuscript ms[], no &. Never put an & on an array parameter — §4.4.


Rep 9 — remove_by_name and the shift

The payoff of the chapter. Declare Manuscript ms[10], brace-fill the first five, count = 5. Write void list_all(const Manuscript ms[], int count) and bool remove_by_name(Manuscript ms[], int& count, const string& target) — find the index, return false on -1, otherwise shift every later record down with a single ms[j] = ms[j + 1]; and decrement count. Print the list, remove "Codex Vaticanus" (printing the returned bool under boolalpha), print again, then try "Codex Alexandrinus" and report count.

Expected output:

Before (count = 5):
  0: P52 (century 2, papyrus)
  1: Codex Sinaiticus (century 4, uncial)
  2: Codex Vaticanus (century 4, uncial)
  3: Codex Bezae (century 5, uncial)
  4: Bodmer Papyri (century 3, papyrus)

remove_by_name("Codex Vaticanus") returned true
After (count = 4):
  0: P52 (century 2, papyrus)
  1: Codex Sinaiticus (century 4, uncial)
  2: Codex Bezae (century 5, uncial)
  3: Bodmer Papyri (century 3, papyrus)

remove_by_name("Codex Alexandrinus") returned false
count is still 4

One assignment moved a name, a century and a type together. Last week that was three assignments that had to stay in lockstep forever, and one of them was always the one you forgot.


Reps 10–14: Structs In and Out of Functions

Rep 10 — By value versus by reference

One record: Manuscript m = {"Codex Bezae", 5, "uncial"};. Write all three from §4.4 — print_manuscript(const Manuscript&), mark_disputed_locally(Manuscript m) and mark_disputed(Manuscript& m), the last two both appending " (disputed)" to m.type. Print, call the by-value version, print, call the by-reference version, print. Label each block.

Expected output:

Before:
Codex Bezae (century 5, uncial)

After mark_disputed_locally (by value):
Codex Bezae (century 5, uncial)

After mark_disputed (by reference):
Codex Bezae (century 5, uncial (disputed))

The middle block is the whole rep. The function ran. No error, no warning, nothing changed. One & is the entire difference.


Rep 11 — add() and the int& you will forget

Set const int MAX = 4; at file scope. Declare Manuscript ms[MAX] = {}; and int count = 0;. Write bool add(Manuscript ms[], int& count, const Manuscript& m) — guard on count >= MAX, assign into ms[count], increment, return true — plus a list_all that prints (empty) at zero.

Under boolalpha, add all five standard records in order, printing each return value. Then print count and the list.

Expected output:

add P52:               true
add Codex Sinaiticus:  true
add Codex Vaticanus:   true
add Codex Bezae:       true
add Bodmer Papyri:     false

count = 4 (MAX = 4)
  0: P52 (century 2, papyrus)
  1: Codex Sinaiticus (century 4, uncial)
  2: Codex Vaticanus (century 4, uncial)
  3: Codex Bezae (century 5, uncial)

The false is the guard clause working. A full array refuses politely instead of writing off the end.


Rep 12 — Return a Summary

Back to five records. Add struct Summary { int count; int earliest_century; int latest_century; double average_century; }; and Summary summarize(const Manuscript ms[], int count) — value-initialize with Summary s{}, guard-clause return s; when count == 0, otherwise scan once for earliest, latest and total, then cast. Print all four fields, then call summarize(ms, 0) and print its count, earliest and average.

Expected output:

Records:  5
Earliest: century 2
Latest:   century 5
Average:  century 3.6

Empty case: count = 0, earliest = 0, average = 0

A function returns one thing; when you need four, make the one thing a struct (§4.5). Chapter 3 made you fake this with output parameters. You are done with that.


Rep 13 — A struct inside a struct

Add struct Date { int year; int month; int day; }; above Manuscript, and give Manuscript a Date catalogued; field between name and century. Update print_manuscript to end with catalogued YYYY-M-D.

Build a the long way — Manuscript a{}; then name, century, type, a.catalogued.year = 2011, a.catalogued.month = 3, and deliberately never assign the day. Build b with nested braces: {"Codex Vaticanus", {2015, 6, 30}, 4, "uncial"}. Print both, then print a.catalogued.day and b.catalogued.month + 1.

Expected output:

P52 (century 2, papyrus) catalogued 2011-3-0
Codex Vaticanus (century 4, uncial) catalogued 2015-6-30
a.catalogued.day was never assigned; it holds 0
b.catalogued.month + 1 = 7

The 0 in 2011-3-0 is the {} reaching two levels down. The last line proves a nested field is an ordinary int.


Rep 14 — Bug drill: the four that compile clean

The dangerous bugs do not talk to you. Build one correct program covering the chapter: MAX = 6, Manuscript ms[MAX] = {}, count = 0, and the functions list_all, add, find_index, remove_by_name, and average_century (guard count == 0, then cast). In main, add the five standard records, print count = 5, list them, print the average, remove "Codex Vaticanus", print the new count, list again.

Expected output (the correct version):

count = 5
  0: P52 (century 2, papyrus)
  1: Codex Sinaiticus (century 4, uncial)
  2: Codex Vaticanus (century 4, uncial)
  3: Codex Bezae (century 5, uncial)
  4: Bodmer Papyri (century 3, papyrus)
Average century: 3.6

After removing Codex Vaticanus, count = 4
  0: P52 (century 2, papyrus)
  1: Codex Sinaiticus (century 4, uncial)
  2: Codex Bezae (century 5, uncial)
  3: Bodmer Papyri (century 3, papyrus)

Now make each of these one-line changes, one at a time, compile (all four are clean — zero errors, zero warnings), run, match, and put the line back.

(A) Change int& count to int count in add. §4.13 bug 14 — §4.4 calls it the most common Week 4 bug that compiles cleanly.

count = 0
  (empty)
Average century: 0

After removing Codex Vaticanus, count = 0
  (empty)

Five successful add calls and an empty list. The count you incremented was a copy that died at the closing brace.

(B) Change static_cast<double>(total) / count to total / count. One line differs:

Average century: 3

(C) Change i < count to i <= count in list_all. Watch both lists:

count = 5
  0: P52 (century 2, papyrus)
  1: Codex Sinaiticus (century 4, uncial)
  2: Codex Vaticanus (century 4, uncial)
  3: Codex Bezae (century 5, uncial)
  4: Bodmer Papyri (century 3, papyrus)
  5:  (century 0, )
Average century: 3.6

After removing Codex Vaticanus, count = 4
  0: P52 (century 2, papyrus)
  1: Codex Sinaiticus (century 4, uncial)
  2: Codex Bezae (century 5, uncial)
  3: Bodmer Papyri (century 3, papyrus)
  4: Bodmer Papyri (century 3, papyrus)

Two different lies from one =. The first list invents a blank sixth record; the second shows Bodmer Papyri twice, because the shift copied it down and never erased the original. You are reading a slot that is no longer part of the list. It is only this tidy because the array was declared = {} — drop those braces and you are in §4.13 bug 13, where the same read printed 150 MB of binary garbage.

(D) Delete the count--; at the end of remove_by_name.

After removing Codex Vaticanus, count = 5
  0: P52 (century 2, papyrus)
  1: Codex Sinaiticus (century 4, uncial)
  2: Codex Bezae (century 5, uncial)
  3: Bodmer Papyri (century 3, papyrus)
  4: Bodmer Papyri (century 3, papyrus)

The shift ran perfectly. The list still has five entries because you never told it otherwise.

Four bugs, four silences, four different wrong answers — and every one is on §4.18’s pre-flight checklist. That list is not generic advice. It is this rep.


Reps 15–18: Cumulative Review — Midterm Prep (Weeks 1–4)

Reps 15 and 16 are reading and prediction drills: they prepare Part A, the auto-graded code-reading quiz. Reps 17 and 18 are build-it-cold drills: they prepare Part B, the take-home practical. Do all four, in order, before you sit either part.

Rep 15 — Trace it on paper first (Part A)

The highest-yield drill in this file. Do it in the stated order or you get nothing from it.

Below is a complete program. Do not type it yet. Read it, and on paper work out all six lines it prints — every name, every number. Then type it, compile, run, and compare.

#include <iostream>
#include <string>
using namespace std;

struct Reader {
    string name;
    int stamina;
    int chapters;
};

int cost(int chapters) {
    return 10 + chapters * 3;
}

void read_phase(Reader team[], int count, int chapters) {
    for (int i = 0; i < count; i++) {
        if (team[i].stamina < 40) {
            continue;
        }
        team[i].chapters += chapters;
        team[i].stamina -= cost(chapters);
    }
}

int main() {
    const int N = 4;
    Reader team[N] = {
        {"Maya", 100, 0},
        {"Lin", 46, 0},
        {"Tomas", 39, 0},
        {"Sade", 60, 0}
    };

    for (int day = 1; day <= 2; day++) {
        read_phase(team, N, 2);
    }

    int total = 0;
    for (int i = 0; i < N; i++) {
        cout << team[i].name << ": " << team[i].chapters
             << " chapters, stamina " << team[i].stamina << endl;
        total += team[i].chapters;
    }
    cout << "Total: " << total << endl;
    cout << "Average: " << static_cast<double>(total) / N << endl;
    return 0;
}

Expected output:

Maya: 4 chapters, stamina 68
Lin: 2 chapters, stamina 30
Tomas: 0 chapters, stamina 39
Sade: 4 chapters, stamina 28
Total: 10
Average: 2.5

Four traps, and Part A uses all of them:

  1. cost(2) is 16, not 6. 10 + 2 * 3 — precedence, Week 1.
  2. Lin reads on day 1 and skips day 2. She starts at 46, which is not below 40, so she reads and drops to 30 — and 30 is below 40. The boundary flips between iterations. Exactly the “Lin” case §4.12 warns about.
  3. Tomas never reads at all. He starts at 39 and continue skips the cost too, so nothing about him ever changes.
  4. The average is 2.5, not 2. Ten chapters over four readers; without the cast it prints 2.

Missing any of the four on paper is not a failure — it is the most useful thing you will learn this week, and it cost five minutes instead of fifty exam points. Re-trace until the paper matches the terminal.


Rep 16 — Find the bug by reading (Part A)

The spec says two things: report the first evidence item with the highest strength, and print the average strength as a fraction. This program does neither. Find both defects by reading only, write down what you think it prints, then run it.

int index_of_strongest(const Evidence inv[], int count) {
    int best = 0;
    for (int i = 0; i < count; i++) {
        if (inv[i].strength >= inv[best].strength) {
            best = i;
        }
    }
    return best;
}

double average_strength(const Evidence inv[], int count) {
    int total = 0;
    for (int i = 0; i < count; i++) {
        total += inv[i].strength;
    }
    return total / count;
}

struct Evidence { string claim; int strength; };, and the array holds, in order: Death by crucifixion 10, Empty tomb tradition 8, Post-mortem appearance tradition 9, Conversion of Paul 10, Conversion of James 7. (Those ratings are the book’s own weighting — sample data for the exercise, not scholarly figures, exactly as §4.7 says.) main prints Strongest: <claim> (<strength>) and Average strength: <value>.

Expected output — the buggy version:

Strongest: Conversion of Paul (10)
Average strength: 8

Now fix both — > instead of >=, and static_cast<double>(total) / count — and rerun.

Expected output — the fixed version:

Strongest: Death by crucifixion (10)
Average strength: 8.8

The >= only misbehaves when there is a tie: strict > keeps the first maximum, >= keeps the last. §4.12 flags the identical trap where Marcus and Sade both land on 20. Neither operator is wrong in general. Not knowing which one you wrote is.


Rep 17 — Build it cold: the Evidence Log (Part B)

From scratch, blank file, about 30 minutes. Patterns 1, 2 and 3 from §4.11 — the combination Part B is built out of.

struct Evidence { string claim; string source; int strength; };, const int MAX_EVIDENCE = 8;, an array brace-filled with four records and int count = 4;:

claimsourcestrength
Death by crucifixionTacitus, Annals 15.44; all four Gospels10
Empty tomb traditionMark 16; multiple attestation8
Post-mortem appearance tradition1 Corinthians 15:3-89
Conversion of Paul (a persecutor)Acts 9; Galatians 110

Write print_evidence(const Evidence&, int number) printing two lines per record, list_all (prints (empty) at zero), average_strength (guard, then cast), and bool remove_at(Evidence inv[], int& count, int index) that rejects an out-of-range index and otherwise shifts down and decrements.

main is an EOF-safe menu loop: if (!(cin >> choice)) { … break; } then cin.ignore(numeric_limits<streamsize>::max(), '\n');#include <limits>. Options: 1 list, 2 average, 3 remove by number (1-based, read the same EOF-safe way), 4 exit; anything else prints Invalid choice..

Expected output for the input 1, 2, 3, 2, 1, 4 — one per line:


--- Evidence Log (4 records) ---
1) List  2) Average strength  3) Remove by number  4) Exit
Choice: 
  1. Death by crucifixion (10/10)
     source: Tacitus, Annals 15.44; all four Gospels
  2. Empty tomb tradition (8/10)
     source: Mark 16; multiple attestation
  3. Post-mortem appearance tradition (9/10)
     source: 1 Corinthians 15:3-8
  4. Conversion of Paul (a persecutor) (10/10)
     source: Acts 9; Galatians 1

--- Evidence Log (4 records) ---
1) List  2) Average strength  3) Remove by number  4) Exit
Choice: 
Average strength: 9.25

--- Evidence Log (4 records) ---
1) List  2) Average strength  3) Remove by number  4) Exit
Choice: 
Number to remove: 
Removed.

--- Evidence Log (3 records) ---
1) List  2) Average strength  3) Remove by number  4) Exit
Choice: 
  1. Death by crucifixion (10/10)
     source: Tacitus, Annals 15.44; all four Gospels
  2. Post-mortem appearance tradition (9/10)
     source: 1 Corinthians 15:3-8
  3. Conversion of Paul (a persecutor) (10/10)
     source: Acts 9; Galatians 1

--- Evidence Log (3 records) ---
1) List  2) Average strength  3) Remove by number  4) Exit
Choice: 
Goodbye.

That is the program’s own output, captured with the six digits fed in from a file. Type them by hand instead and your keystrokes also appear after each prompt — everything the program prints is unchanged. Both prompts end with endl precisely so this stays checkable either way.

Check the arithmetic: 10 + 8 + 9 + 10 = 37 over 4 is 9.25; removing the 8 leaves 29 over 3, which is 9.66667. Whole numbers there mean a missing cast.

Then run it once more with no input at all (< /dev/null, or press Ctrl+D at the first prompt in OnlineGDB).

Expected output:


--- Evidence Log (4 records) ---
1) List  2) Average strength  3) Remove by number  4) Exit
Choice: 

(no more input - exiting)

If yours spins forever printing menus, you wrote cin >> choice; without the check — §4.7 explains why a failed read sets choice to 0 and jams the stream. A program you cannot get out of is a program you cannot submit.


Rep 18 — Dress rehearsal: Campus Outreach Week (Part B)

From scratch, blank file, timed at 60 minutes. Part B gives about three hours for a problem of this shape, so an hour here means margin; if it takes two, build a second variant — different roles, costs, phases — before Session 4.

Named constants: DAYS = 5, MAX_ENERGY = 100, MIN_ENERGY_TO_SERVE = 30, MORNING_RECOVERY = 4, EVENING_RECOVERY = 6.

struct Volunteer { string name; string role; int energy; int conversations; int days_rested; };, and an array of five brace-initialized in this order — Maya/greeter/100, Marcus/speaker/100, Lin/driver/90, Jonah/greeter/100, Sade/speaker/55 — all at 0 conversations and 0 days rested.

Two dispatch helpers over the role string, each ending in an unconditional return (§4.13, bug 11): energy_cost — greeter 12, speaker 20, else 16; conversations_for — greeter 5, speaker 3, else 4.

Three phase functions that walk the whole array, plus recover(Volunteer&, int) which adds energy and clamps at MAX_ENERGY:

  • morning_phase — recover MORNING_RECOVERY.
  • outreach_phase — if energy < MIN_ENERGY_TO_SERVE, days_rested++ and nothing else; otherwise add conversations_for(role) and subtract energy_cost(role).
  • evening_phase — recover EVENING_RECOVERY.

main prints a two-line banner, runs for (int day = 1; day <= DAYS; day++) over the three phases in order, then reports: one line per volunteer via const Volunteer& v = team[i]; — name, role, conversations, days rested, energy, and steady if they never rested else needed rest — then total conversations, the average per volunteer per day (total / (N * DAYS), cast), the volunteer with the most conversations found with strict >, and find_by_name for "Lin" and "Priscilla".

Expected output:

=== Campus Outreach Week ===
5 volunteers, 5 days.

=== After 5 days ===
  Maya (greeter): 25 conversations, 0 rested, energy 86 - steady
  Marcus (speaker): 15 conversations, 0 rested, energy 46 - steady
  Lin (driver): 20 conversations, 0 rested, energy 60 - steady
  Jonah (greeter): 25 conversations, 0 rested, energy 86 - steady
  Sade (speaker): 12 conversations, 1 rested, energy 25 - needed rest

Total conversations: 97
Average per volunteer per day: 3.88
Most conversations: Maya with 25

find_by_name("Lin")       = 2
find_by_name("Priscilla") = -1

Five things to check, in the order they usually go wrong:

  1. Maya and Jonah both finish on 25, and Maya wins. Strict > keeps the first maximum; >= prints Jonah. Rep 16 again, in a bigger program.
  2. Sade rests exactly once, on day 4. She enters that morning at 29 — one point under the threshold — rests, and recovers enough to serve again on day 5. Rested twice or never? Re-check the clamp or the phase order.
  3. Maya never drops below 80 despite serving all five days: +4 − 12 + 6 is a net −2 a day, and the morning clamp throws away the surplus on day 1.
  4. The average is 3.88. 97 conversations over 25 volunteer-days; without the cast it prints 3.
  5. Lin is index 2, Priscilla is −1. Chapter 3’s -1-means-not-found convention, still earning its keep.

Match the report line for line on the first run and you are ready for Part B. If you do not, the mismatched line names the phase to go print-debug — §4.16, rung 3.


Done? One Last Thing.

Open a fresh file, from_memory_4.cpp. Nothing else open. No notes, no earlier code, no AI. Write, from memory:

  1. const int MAX_WITNESSES = 3; and struct Witness { string name; string testimony; int credibility; };
  2. void print_witness(const Witness& w) — one line, name - testimony (credibility N).
  3. bool add_witness(Witness list[], int& count, const Witness& w) with a MAX_WITNESSES guard.
  4. double average_credibility(const Witness list[], int count) returning 0.0 on empty, otherwise the true fractional average.
  5. A main that value-initializes the array and count = 0, then under boolalpha adds Mary Magdalene / empty tomb / 9, Peter / appearance to the Twelve / 7, James / appearance to James / 6, and Paul / Damascus road / 10 — printing each return value — then prints count, every witness, and the average. (Those credibility numbers are sample data for the exercise, not scholarly figures.)

Expected output:

add Mary Magdalene: true
add Peter:          true
add James:          true
add Paul:           false

count = 3
Mary Magdalene - empty tomb (credibility 9)
Peter - appearance to the Twelve (credibility 7)
James - appearance to James (credibility 6)
Average credibility: 7.33333

The false and the 7.33333 are the lines that matter: the guard held, and the cast held. Compile clean, match all nine lines first try, and you have the move — struct, array of structs, int&, const&, guard clause and cast, all from memory. That is the entire Week 4 toolkit and most of the Part B rubric.

Two or three attempts is still a pass, but do it once more tomorrow before the exam. If you could not start, do not go to Part B yet: back to Reps 9, 11 and 12, then retake the §4.15 checkpoint.


Up next: the Midterm. Take Part A — Code Reading & Tracing (50 points, auto-graded) in Canvas first, and read every rationale, including on the ones you got right. Then Part B — Mission Trip Simulator (100 points, take-home practical, about three focused hours, AI off) inside its 24-hour window. Reps 15 and 16 were your Part A warm-up; Reps 17 and 18 were your Part B warm-up. Re-read §4.18’s pre-flight checklist before you start writing, and see Appendix A if you need a refresher on sharing an OnlineGDB link.