Arrays Labs (7)
← Module home · Every lab: function team (house rules):
constreaders, plain writers, narratormain. Test data is fixed — expected behaviour is a table you verify against, not a vibe. Solutions are separated inside each lab; attempt the team design first.
Lab 1 — Student marks analyzer
Scenario. A class tutor needs a marks report: the syllabus’s Unit 09 project in lab form — the mini-project is its full version.
Requirements. Read up to 40 marks (validated 0–100; sentinel −1 ends input). Report: count, average (2 dp), highest + who (position), lowest + who, pass count (≥ 40), and the full list with pass/fail flags.
Test data (fixed): 55 38 91 40 62 -1
Expected behaviour:
| field | value |
|---|---|
| count | 5 |
| average | 57.20 |
| highest | 91 (student 3) |
| lowest | 38 (student 2) |
| passed | 4 |
Solution.
#include <iostream>
#include <iomanip>
using namespace std;
constexpr int MAX_STUDENTS = 40;
int readMarks(int marks[], int capacity);
void printReport(const int marks[], int n);
int sumOf(const int marks[], int n);
int indexMax(const int marks[], int n);
int indexMin(const int marks[], int n);
int countAtLeast(const int marks[], int n, int cut);
int main() {
int marks[MAX_STUDENTS];
int n = readMarks(marks, MAX_STUDENTS);
if (n == 0) { cout << "No marks.\n"; return 0; }
printReport(marks, n);
return 0;
}
int readMarks(int marks[], int capacity) {
int n = 0, value;
cout << "Mark (0-100, -1 to end): ";
cin >> value;
while (value != -1 && n < capacity) {
if (value >= 0 && value <= 100) { marks[n] = value; n += 1; }
else cout << "0-100 only.\n";
if (n < capacity) { cout << "Mark (-1 to end): "; cin >> value; }
else break;
}
return n;
}
void printReport(const int marks[], int n) {
cout << fixed << setprecision(2);
cout << "Count: " << n << " average: " << static_cast<double>(sumOf(marks, n)) / n << '\n';
cout << "Highest: " << marks[indexMax(marks, n)]
<< " (student " << indexMax(marks, n) + 1 << ")\n";
cout << "Lowest: " << marks[indexMin(marks, n)]
<< " (student " << indexMin(marks, n) + 1 << ")\n";
cout << "Passed (>=40): " << countAtLeast(marks, n, 40) << " of " << n << '\n';
for (int i = 0; i < n; i = i + 1)
cout << "Student " << setw(2) << i + 1 << ": " << setw(3) << marks[i]
<< (marks[i] >= 40 ? " pass" : " FAIL") << '\n';
}
int sumOf(const int m[], int n) { int t = 0; for (int i = 0; i < n; i++) t += m[i]; return t; }
int indexMax(const int m[], int n) { int b = 0; for (int i = 1; i < n; i++) if (m[i] > m[b]) b = i; return b; }
int indexMin(const int m[], int n) { int b = 0; for (int i = 1; i < n; i++) if (m[i] < m[b]) b = i; return b; }
int countAtLeast(const int m[], int n, int cut) { int c = 0; for (int i = 0; i < n; i++) if (m[i] >= cut) c++; return c; }
Explanation. Five const reading machines + one writer + one printer — the Lesson 3 team verbatim. The champions return indices, which is what lets the report say who: indexMax answers “position”, one dereference answers “value”. The reader validates per value and doesn’t count rejects toward n — the capacity check sits before every prompt so the array can never overflow. Note the sum cast inside the printer: 273/5 must be 57.2, not 57 — the integer-division rule applied at the display layer.
Extensions. ⭐ Grade histogram (A/B/C/D/F tallies — tally pass). ⭐⭐ Median without sorting for odd n (the middle box — after sorting, which is next unit; here: count how many are ≤ each candidate). ⭐⭐⭐ Find all students within 5 marks of the average — two passes over stored data, the Functions C1 wall gone.
Lab 2 — Sales analysis
Scenario. A shop logs units sold per day for a week (7 days, Mon–Sun). Report per-day and per-week figures, and find the best day safely — including a lookup feature driven by user input.
Requirements. Fixed array of 7 sales (read, validated ≥ 0). Report: total, daily average, best day (name + units), worst day. Then a lookup loop: read a day number (1–7) and print that day’s units — until 0 quits. Out-of-range lookups must be rejected without indexing.
Test data: 12 8 0 15 9 22 5 (Mon..Sun)
Expected behaviour:
| field | value |
|---|---|
| total | 71 |
| average | 10.14 |
| best | Saturday, 22 |
| worst | Sunday, 5 |
| lookup 4 | Thursday: 15 |
| lookup 9 | rejected, no crash |
| lookup 0 | quits |
Solution.
constexpr int DAYS = 7;
int readSales(int sales[], int days);
void printAnalysis(const int sales[], int days);
int indexMax(const int s[], int n);
int indexMin(const int s[], int n);
void lookupLoop(const int sales[], int days);
void printDayName(int i); // 0-based index -> name
int main() {
int sales[DAYS];
readSales(sales, DAYS);
printAnalysis(sales, DAYS);
lookupLoop(sales, DAYS);
return 0;
}
void lookupLoop(const int sales[], int days) {
int d;
cout << "Day to look up (1-7, 0 to quit): ";
cin >> d;
while (d != 0) {
if (d >= 1 && d <= days) cout << "Day " << d << ": " << sales[d - 1] << '\n';
else cout << "Days are 1-7.\n"; // NO indexing on the bad path
cout << "Day (0 to quit): ";
cin >> d;
}
}
// readSales: validated fill (as Lab 1); printAnalysis: sum/average + champions + names.
Explanation. The lab’s teaching point is the lookup loop: user input as index is the classic bounds hazard, and the fix is procedural — validate 1..7 before touching sales[d-1]. Note the conversion dance: humans count 1–7, boxes count 0–6; d - 1 is the bridge, and it’s the validated d. Day names come from a printDayName switch — an array of names arrives properly with the Strings module, so the switch is the honest course-tool for today.
Extensions. ⭐ Flag days below half the average (Below target) — two passes over stored data. ⭐⭐ Week-over-week: read a second week and print per-day change (+/−) — two arrays, one pass. ⭐⭐⭐ Best pair of consecutive days (the C14 window with k = 2).
Lab 3 — Temperature analysis
Scenario. A weather station logs one temperature per hour for 24 hours. Report daily statistics and detect a cold snap.
Requirements. Read 24 integers (−50..50, validated). Report: min, max, average (1 dp), the hours (0–23) at which min and max occurred (if ties, first), and the count of “freezing” readings (< 0). Then: longest cold streak — most consecutive hours below 0 (print start hour and length; 0 if never).
Test data: 5 6 4 -1 -2 -3 1 8 12 14 15 16 17 16 15 13 11 9 6 4 2 0 -1 -2
Expected behaviour:
| field | value |
|---|---|
| min | −3 (hour 5) |
| max | 17 (hour 12) |
| average | 6.9 |
| freezing count | 5 (hours 3, 4, 5, 22, 23) |
| longest cold streak | hours 3–5, length 3 |
Solution (the streak core).
// longest run of consecutive values < 0
int run = 0, bestRun = 0, bestStart = -1, start = -1;
for (int i = 0; i < 24; i = i + 1) {
if (temp[i] < 0) {
if (run == 0) start = i; // streak begins
run += 1;
if (run > bestRun) { bestRun = run; bestStart = start; }
} else {
run = 0; // streak broken
}
}
// report bestRun (0 = never) and bestStart
Explanation. Min/max/average/count are the standard passes; the cold streak is the new machine — the run-tracking pattern (current run vs best run, two counters with different lifetimes) wearing temperatures. The trace discipline: run resets on any non-negative box; bestRun only grows; bestStart is captured the moment the best grows. Ties for min/max resolve to the first occurrence because champions update on strict comparisons — a policy, stated.
Extensions. ⭐ Print a 24-hour sparkline: one char per hour (_ ≥ 0, * < 0). ⭐⭐ Diurnal range per half-day (hours 0–11 vs 12–23) — two half-grid passes. ⭐⭐⭐ Second-largest temperature (C4) and the hour it occurred.
Lab 4 — Inventory analysis
Scenario. A warehouse tracks stock for 10 product codes (100–109). Report stock levels, flag reorder lines, and support a restock transaction loop.
Requirements. int stock[10] parallel to codes 100–109 (read initial levels, validated ≥ 0). Report: per-product line (code, level, REORDER if < 5), total items, and the code needing restock most urgently (lowest level; ties → lowest code). Then a transaction loop: read code qty — positive qty adds stock, negative removes (floor at 0, report attempted over-removal), code 0 quits, unknown codes rejected without indexing.
Test data: levels 12 3 45 0 8 3 60 7 2 20
Expected behaviour:
| field | value |
|---|---|
| reorder flags | codes 101, 103, 105, 108 |
| total items | 160 |
| most urgent | code 103 (0) |
| transaction 101 +10 | level 13 |
| transaction 103 −5 | rejected: only 0 in stock |
| transaction 999 +4 | rejected: unknown code |
Solution (the index-bridge core).
// code 100..109 -> box index 0..9
int idx = code - 100;
if (idx >= 0 && idx < 10) { /* safe to use stock[idx] */ }
else cout << "Unknown code\n";
Explanation. The lab formalizes code-to-index bridging: when data is an index in disguise, the bridge (code - 100) plus a range guard is the whole safety story — the tally mechanic with transactions. The over-removal floor (level = max(0, level + qty)-shaped decision) is a business rule living in the transaction machine, not the reader. “Most urgent” is indexMin over the levels with the tie policy (first wins → lowest code, because the array is code-ordered).
Extensions. ⭐ Restock suggestion: print order quantities to bring every flagged product to 10. ⭐⭐ Two warehouses: parallel arrays, consolidated report — function signatures unchanged, main grows (the stateless test). ⭐⭐⭐ Value the inventory: read a price per code and report total stock value — three parallel arrays, one pass.
Lab 5 — Survey response analysis
Scenario. 10 students answer a 5-question survey; each answer is 1–5 (with 0 = skipped). Report per-question tallies and the overall skip rate.
Requirements. Read 10×5 answers into int answers[10][5] (validated 0–5). Report per question: tally of 1–5 and skip count; the mode per question (ties → lowest option); overall: total skips and skip percentage. Then print a per-student completion list (Student 4: 3/5).
Test data (10 rows, one per student): 1 3 2 4 5 / 0 3 2 4 5 / 1 0 2 4 5 / 1 3 0 4 0 / 5 3 2 4 5 / 1 3 2 0 5 / 1 3 2 4 5 / 2 3 3 4 5 / 1 3 2 4 5 / 1 3 2 4 5
Expected behaviour (for the 10 rows):
| field | value |
|---|---|
| Q1 tally (1..5) | 6, 1, 0, 0, 1 · skips 2 |
| Q2 mode | 3 (9 votes) |
| Q4 skips | 1 |
| total skips | 6 |
| skip rate | 12.0% |
| Student 4 | 3/5 |
Solution (per-question tally core).
int tally[6] = {0}; // index = answer (0 = skip)
for (int r = 0; r < students; r = r + 1) {
int a = answers[r][q]; // freeze question q (outer loop over q)
if (a >= 0 && a <= 5) tally[a] += 1; // data-as-index, guarded
}
// mode: champion over tally[1..5]; skips = tally[0]
Explanation. The 2D array organizes the data; the analysis is still 1D passes — freeze one dimension, walk the other (Lesson 4 §3): outer loop over questions, inner over students, tally reset per question (the M2 reset trap is the bug this lab catches). Data-as-index with 0 = skip makes tally[0] the skip counter for free — the category is just another index. Completion per student is the mirrored pass (freeze r, walk c).
Extensions. ⭐ Star-bar per option per question (#####: 5) — printer over the tally. ⭐⭐ Questions ranked by skip rate — sort is next unit; here: find max/min skip counts and print their question numbers (champion over question totals). ⭐⭐⭐ Agreement score: for each pair of questions, count students who answered both identically — a 5×5 co-occurrence matrix (the tally idea squared).
Lab 6 — Matrix calculator
Scenario. A 3×3 matrix workbench: enter two matrices, compute sums, transposes, diagonal facts, and (the summit) the product.
Requirements. Read two 3×3 integer matrices (row by row, validated). Menu: 1 sum, 2 difference, 3 transpose of A, 4 main-diagonal sums of A and B, 5 product A×B, 0 quit. All operations as functions against constexpr int N = 3; after each operation print the result matrix with aligned columns.
Test data: A = { {1,2,3},{4,5,6},{7,8,9}}, B = { {9,8,7},{6,5,4},{3,2,1}}
Expected behaviour:
| operation | result |
|---|---|
| A + B | all boxes 10 |
| A − B | {-8,-6,-4},{-2,0,2},{4,6,8} |
| Aᵀ | { {1,4,7},{2,5,8},{3,6,9}} |
| diag sums | A: 15, B: 15 |
| A×B | { {30,24,18},{84,69,54},{138,114,90}} |
Solution (product core).
void multiply(const int a[][N], const int b[][N], int prod[][N]) {
for (int r = 0; r < N; r = r + 1)
for (int c = 0; c < N; c = c + 1) {
prod[r][c] = 0; // per-box accumulator
for (int k = 0; k < N; k = k + 1)
prod[r][c] += a[r][k] * b[k][c];
}
}
// sumOf, subtract, transpose, diagonalSum: from Lesson 4 §4 / [S30-S32](/Programming-Fundamentals-Using-C-/arrays/exercises.html#s30--add-and-transpose)
Explanation. Every operation is a Lesson 4 translation, and the lab’s discipline is the 2D parameter rule: constexpr int N = 3 at file scope, every function signed (...[][N], ...) — the column count is law. A×B verifies by hand first (prod[0][0] = 1×9+2×6+3×3 = 30 ✓) — the C10 prover habit. The per-box reset before the k-loop is the third accumulator-in-a-nest of the course; forgetting it is the lab’s seeded-bug classic. Reading is a nested fill (row by row), printing a nested walk with setw — the grid pair from iteration D11, now with real data.
Extensions. ⭐ Scalar multiply (k read). ⭐⭐ Symmetry and diagonal-matrix checks on A (C11). ⭐⭐⭐ Power: A² = A×A, then A³ — and verify A² × A == A³ (multiplication associativity, one more correctness check for free).
Lab 7 — Simple statistics calculator
Scenario. A statistics bench: any dataset in, the full classic report out. This is the convergence lab — every pass from Lesson 2 in one program.
Requirements. Read up to 100 values (validated; sentinel −1). Report: n, min, max, range, sum, mean (2 dp), the count above mean, and a 10-band histogram (like C7, bands of the data’s own range: band = (v - min) * 10 / (range + 1) — derive and defend this formula). Guard every division.
Test data: 12 5 8 20 3 15 8 12 -1
Expected behaviour:
| field | value |
|---|---|
| n, min, max, range | 8, 3, 20, 17 |
| sum, mean | 83, 10.38 |
| above mean | 3 (12, 20, 15) |
| histogram | band 0:1, band 1:1, band 2:2, band 3:0, band 4:0, band 5:2, band 6:1, band 7:0, band 8:0, band 9:1 |
Solution (band formula core).
// after computing min, max, range = max - min:
int band = (v - min) * 10 / (range + 1); // 0..9, never 10 (the +1)
hist[band] += 1;
Explanation. The band formula is the lab’s intellectual core: (v - min) shifts the data’s floor to 0; * 10 / (range + 1) squeezes it into exactly 10 bands, and the +1 guarantees v == max lands in band 9, never 10 — work it for min=3, max=20: v=20 → 17×10/18 = 9 ✓. Every guard from the unit appears: n ≥ 1 before mean, range ≥ 0 always (max ≥ min by construction), data-as-index validated by the formula’s own construction (but keep the assert-style comment). The histogram is the tally over derived indices — the same trick as C7, generalized to any range.
Extensions. ⭐ Print the histogram with setw-aligned labels and star bars. ⭐⭐ Median without sorting for odd n: the box at position n/2 of the sorted data — approximate honestly here by counting how many values are ≤ each candidate (or descope with a comment naming sorting). ⭐⭐⭐ Standard deviation via the two-pass formula: mean first, then the sum of squared differences — the accumulator idiom over a derived series, and the naturalpreview of Stage D’s statistical work.
After the labs
Seven datasets, one toolkit: draw the boxes, freeze-or-walk, guard every index, const the readers. The mini-project now assembles the full marks-analyzer with a menu, persistence of your choice, and a write-up — the Unit 09 capstone the syllabus promised.