Skip to content

Practice Lab 1 — Student profile records, without classes

Difficulty: Beginner · Estimated time: 2–3 hours · Module 1: C++ and the OOP Mindset

This lab is deliberately clumsy. You are going to build a working program with no classes at all, feel where it hurts, and write down why — which is the reason object orientation exists.


At a glance

Difficulty Beginner
Estimated time 2–3 hours
Module Module 1 — C++ and the OOP Mindset
Concepts practised Parallel arrays, free functions, long parameter lists, global state, identifying objects from a problem statement
Prerequisite Set Up Your Toolchain, Your First Program
Class design none — deliberately

Scenario

A department office keeps a list of student profiles for a small programme. Each profile has a roll number, a name, a programme, a year of study, and a set of marks. The office needs to print the list, look a student up by roll number, add marks, and produce a class average.

You have been asked for a quick program. It is a throwaway: the real system will be written later, and what matters now is that you feel exactly which parts of this version will not survive.


Problem statement

Write a single-file program that stores up to 100 student profiles and offers these operations:

  1. Add a profile (roll number, name, programme, year).
  2. Look a student up by roll number, printing their details and marks.
  3. Add a mark to a student.
  4. Print every profile, in ascending roll-number order.
  5. Print the average mark across all students.

Store the profiles in parallel arrays — one array of roll numbers, one of names, one of programmes, one of years, one of mark counts, and a two-dimensional array of marks. Implement each operation as a free function that takes the arrays and the count.

This is not a strawman. Parallel arrays are what a great deal of real C code does, and they work.


Learning objectives

By the end of this lab you will be able to:

  • Write a working multi-operation program using parallel arrays and free functions.
  • Identify, in your own code, the three symptoms of a missing abstraction: a count threaded everywhere, a set of arrays that must be kept in step, and a function signature that grows with every new field.
  • Name the objects a problem contains, from the problem statement rather than from the code.
  • State, in one sentence each, what a class would group in this program and why the grouping is not merely cosmetic.

Functional requirements

  1. At most 100 profiles; a full list refuses the addition with a message.
  2. A roll number is unique; a duplicate is refused with a message.
  3. An unknown roll number in a lookup prints not found and does not crash.
  4. At most 10 marks per student; the eleventh is refused with a message.
  5. Marks are integers in 0..100; anything else is refused.
  6. Printing is in ascending roll-number order, whatever order the profiles were added in.
  7. The average is computed over all marks of all students, printed to one decimal place; with no marks it prints no marks recorded rather than dividing by zero.
  8. Every operation is a free function; main is a menu loop.

Constraints

  • No classes and no struct in this lab. That is the point of it.
  • One source file, compiled exactly as the course teaches: g++ -std=c++17 -Wall -Wextra -Werror main.cpp -o roster
  • No std::vector, no std::string arrays beyond what you choose for names and programmes — arrays of char are acceptable and instructive.
  • Global arrays, so that the free functions do not need eight parameters. (Notice how this trades one problem for another; you will be asked about that in the reflection.)
  • No warnings.

Starter task

Before writing any of the five operations, write the function signatures only, with empty bodies, and answer these two questions in a file called design-notes.md:

  1. How many parameters does the widest function need, and which of them are really the same object split into pieces?
  2. Which pieces of data must always change together? Name one pair, and say what happens when a caller updates one and forgets the other.

Hint: adding a field of study field later means touching the array declarations, the add function, the print function and the lookup function. Count them in your notes before you add it — you will add it as part of the extension challenge.


Implementation tasks

  1. Declare the parallel arrays and the count at file scope, and write add_profile.
  2. Write find_index(roll_number) — every other operation uses it. Notice that it returns an int index and the special value -1 for "not found", and write one sentence on why that is a fragile contract.
  3. Write add_mark, print_profile, print_all_sorted and average_mark.
  4. Write the menu loop.
  5. Run the test cases below and record the output.
  6. Write reflection.md: the three symptoms you actually felt, each with a line reference from your own code.

Test cases

# Action Expected result
1 Add three profiles, print all Three lines in ascending roll-number order
2 Add a duplicate roll number Refused, with the roll number in the message
3 Look up a roll number that exists Details and marks printed
4 Look up a roll number that does not exist not found, exit status still 0
5 Add a mark to the last profile, print it The mark appears
6 Add eleven marks to one student The eleventh is refused, the first ten remain
7 Add a mark of 101 and of −1 Both refused
8 Average with no marks at all no marks recorded, no division by zero
9 Average of marks 70, 80, 90 80.0
10 Fill the list to 100, then add one more Refused with a message, no out-of-bounds write

Boundary cases to run deliberately: zero profiles; one profile; a full list; the tenth mark.


Expected behaviour

$ ./roster
1) add profile  2) find  3) add mark  4) list  5) average  6) quit
> 1
roll number: 2024003
name: Ayesha Khan
programme: BS Data Science
year: 2
added.
> 4
2024003  Ayesha Khan  BS Data Science  year 2  marks:
> 3
roll number: 2024003
mark: 91
added.
> 3
roll number: 2024999
mark: 91
not found
> 5
average: 91.0   (over 1 mark)
> 6

Extension challenge

Add a field of study to every profile.

Then, in reflection.md, record:

  • Every place you had to edit, as a list.
  • The number of files — which is one, and is the reason this exercise is not yet about files.
  • What a class would have changed about that list.

Debugging challenge

This function is meant to add a mark. It compiles, and it sometimes prints the wrong student's marks:

void add_mark(int roll, int mark) {
    const int index = find_index(roll);
    if (index >= 0) {
        marks[index][mark_count[index]] = mark;
        ++mark_count[index];
    }
}

Reproduce it — add marks to two students in an order that makes the bug visible — then answer:

  1. Which invariant did the code violate, and what is the invariant in one sentence?
  2. Why did the compiler not notice?
  3. Which single line would you change to make the failure loud instead of silent?

Do not fix the function until you have written the answers down.


Reflection questions

  1. Which of your functions had the longest parameter list, and which parameters were really one thing?
  2. Where in your program can two pieces of data get out of step, and what would prevent it?
  3. find_index returns -1 for "not found". Name one caller that could ignore that and what would happen.
  4. Which three names in your program — the arrays and the functions — would you keep as an object's operations if you were allowed classes?
  5. Global state removed the parameter lists. What did it cost you instead?
  6. Write the objects you think this problem contains, as a list of nouns with one responsibility each.

Completion checklist

  • [ ] It compiles with -Wall -Wextra -Werror and no warnings
  • [ ] All ten test cases were run, and the results are in reflection.md
  • [ ] Boundary cases were run: zero profiles, one profile, a full list, the tenth mark
  • [ ] The debugging challenge was reproduced, explained, and only then fixed
  • [ ] design-notes.md records the widest signature and one pair of values that must change together
  • [ ] The extension challenge was done and its edit list recorded
  • [ ] I can name the objects in this problem without looking at the code
  • [ ] I can say, in one sentence each, what three symptoms of missing abstraction appeared in my own work

Related: Lab index · Module 1 — C++ and the OOP Mindset · Next: Practice Lab 2 — Library catalogue in plain structs