Lesson 20 · 20 of 20
Project: a gradebook · C
Combine everything from the C course in a gradebook: structs, arrays, an enum, functions, pointers and a report saved to a file.

This project brings the course together. `#define SUBJECTS 3` gives a fixed value a name: before compiling, every SUBJECTS is replaced by 3. If there were four subjects tomorrow, changing one line would be enough. typedef defines the Result enum (fail, pass, merit) and the Student struct, which holds a name and an array of marks.
average takes a pointer to a const Student: it does not copy the whole student and promises not to change it. With s->marks[i] it adds up the marks in a for. result_of uses the ternary operator twice to pick MERIT from 8.5, PASS from 5 and FAIL otherwise.
In main, group is an array of structs initialised between braces, and sizeof works out how many students there are. counts[3] = {0} starts the counters at zero and, since the enum values are 0, 1 and 2, counts[r]++ adds one to each student's result. labels[r] gives its name as text.
The same loop writes each row on the screen with printf and into report.txt with fprintf, separated by ; so it can be opened in a spreadsheet. At the end, fclose saves the report and the summary is printed. In DMarlon Studio, create a Console target project, paste the code and press F9: you will see the table and the report.txt file in the project folder.
Code · main.c
#include <stdio.h>#define SUBJECTS 3 // a name for a fixed value, replaced before compilingtypedef enum { FAIL, PASS, MERIT } Result;typedef struct { char name[16]; double marks[SUBJECTS]; } Student;double average(const Student *s) { double sum = 0; for (int i = 0; i < SUBJECTS; i++) sum += s->marks[i]; return sum / SUBJECTS;}Result result_of(double avg) { return avg >= 8.5 ? MERIT : (avg >= 5 ? PASS : FAIL);}int main(void) { Student group[] = {{"Ana", {7.5, 8.0, 9.0}}, {"Bruno", {4.0, 5.5, 3.5}}, {"Carla", {9.5, 8.5, 9.0}}, {"Diego", {6.0, 5.0, 7.5}}}; int n = sizeof(group) / sizeof(group[0]), counts[3] = {0}; const char *labels[] = {"fail", "pass", "merit"}; FILE *report = fopen("report.txt", "w"); if (report == NULL) return 1; printf("%-8s %5s %s\n", "Student", "Avg", "Result"); for (int i = 0; i < n; i++) { double avg = average(&group[i]); Result r = result_of(avg); counts[r]++; // the enum value works as an index printf("%-8s %5.2f %s\n", group[i].name, avg, labels[r]); fprintf(report, "%s;%.2f;%s\n", group[i].name, avg, labels[r]); } fclose(report); printf("Merit: %d, pass: %d, fail: %d\n", counts[MERIT], counts[PASS], counts[FAIL]); return 0;}Output · cc main.c -o main && ./main
Student Avg Result Ana 8.17 pass Bruno 4.33 fail Carla 9.00 merit Diego 6.17 pass Merit: 1, pass: 2, fail: 1
Key points
#definenames a value that is used in several places.- An array of
structholds a list of records. - Pass a
constpointer to read a struct without copying it. enumvalues work as array indexes.- Write to the screen and to a file from the same loop.
Exercise
Add a fifth student and a fourth subject (change SUBJECTS to 4), and print at the end the name of the student with the highest average.