DMarlon Studio

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.

C · Project: a gradebook in DMarlon Studio: the example's code and its output in the terminal

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

  • #define names a value that is used in several places.
  • An array of struct holds a list of records.
  • Pass a const pointer to read a struct without copying it.
  • enum values 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.