LearnAI ToolsCareerPractice BuildsPlayContact
C ProgrammingBeginner~1.5 hours

Student Report Card

Generate report cards with grades, percentages, and class rankings for multiple students.

StructuresArraysCalculations

Overview

A student report card generator is a focused, beginner-friendly project that turns raw marks into meaningful, formatted results: totals, percentages, letter grades, and a class ranking. It is a great structures-and-arrays exercise because every student needs several related pieces of data grouped together (name, an array of subject marks, a computed total and percentage), and the whole class needs to be processed and compared as a collection.

By the end of this tutorial you will have a program that accepts marks for any number of students across a fixed set of subjects, computes each student's total, percentage, and letter grade, ranks the whole class by percentage, and prints a clean, formatted report card for every student including their class rank.

What You'll Build
  • A `Student` struct holding a name, an array of subject marks, total, percentage, grade, and rank.
  • Input handling that reads marks for multiple subjects for multiple students.
  • Calculation logic for total marks, percentage, and a letter grade (A/B/C/D/F).
  • A ranking step that sorts students by percentage and assigns 1st, 2nd, 3rd place, and so on.
  • A formatted report card printout for every student, including their rank in the class.

Prerequisites

  • Structures — grouping related fields (name, marks, total, grade) into one `struct Student`.
  • Arrays — an array of marks inside each student, and an array of `Student` structs for the whole class.
  • Loops — nested `for` loops for reading marks across students and subjects.
  • Calculations — computing sums, percentages (`total / (subjects * 100.0) * 100`), and comparing values with `if`/`else if`.
  • Basic sorting logic — comparing and swapping array elements, as used in a simple bubble sort.

Project Structure

Every student is represented by a `struct Student` containing their name, a fixed-size array of marks (one per subject), and computed fields for total, percentage, grade, and rank. All students for a run of the program live in one array, `Student students[MAX_STUDENTS]`, sized by however many students the user says they want to enter.

The program works in three clear phases: first, collect input for every student (name and marks); second, run calculations across the whole array to fill in total, percentage, and grade for each student, then sort a working list by percentage to assign ranks; third, print a formatted report card for every student. Keeping these phases separate — rather than interleaving input, calculation, and printing — makes each function do exactly one job and keeps the overall `main` function easy to read.

Step 1: Define the Student Structure

We fix the number of subjects at 5 for simplicity, and give each student a name, a marks array, and fields to hold the results we will calculate in later steps.

#include <stdio.h> // printf/scanf for all console input and output
#include <string.h> // Reserved for string handling used alongside the fixed-size name buffer
#define MAX_STUDENTS 50 // Upper bound on how many students one run of the program can hold
#define SUBJECTS 5 // Fixed number of subjects every student is marked on
#define NAME_LEN 50 // Max stored length for a student's name
typedef struct {
char name[NAME_LEN]; // Student's name
int marks[SUBJECTS]; // One mark per subject, in a fixed-size array matching SUBJECTS
int total; // Sum of all subject marks, filled in later by calculateResults
float percentage; // Total expressed as a percentage of the maximum possible marks
char grade; // Letter grade derived from percentage
int rank; // Class rank, filled in later by assignRanks
} Student;

Step 2: Input Marks for Every Student

Reading input for the whole class means a loop over students, and inside it, a nested loop over subjects. Marks are validated to fall between 0 and 100, since a report card generator should never accept an impossible score.

void inputStudents(Student students[], int n) {
int i, j; // i indexes students, j indexes subjects within each student
for (i = 0; i < n; i++) {
printf("\n--- Student %d ---\n", i + 1);
printf("Enter name: ");
scanf(" %49[^\n]", students[i].name);
for (j = 0; j < SUBJECTS; j++) {
int m; // Local scratch variable so we can validate before storing into marks[j]
do {
printf("Enter marks for subject %d (0-100): ", j + 1);
scanf("%d", &m);
if (m < 0 || m > 100) { // Reject impossible scores outside the valid range
printf("Invalid marks, please enter a value between 0 and 100.\n");
}
} while (m < 0 || m > 100); // Keep re-asking until a valid mark is entered
students[i].marks[j] = m; // Only store once the value has passed validation
}
}
}

Step 3: Calculate Total, Percentage, and Grade

For each student, the total is the sum of all subject marks, the percentage is the total divided by the maximum possible marks, and the grade is assigned using a simple percentage threshold table. We calculate all three for every student in one pass over the array.

char calculateGrade(float percentage) { // Threshold table mapping percentage to a letter grade
if (percentage >= 90) return 'A';
if (percentage >= 75) return 'B';
if (percentage >= 60) return 'C';
if (percentage >= 40) return 'D';
return 'F'; // Anything below the lowest threshold fails
}
void calculateResults(Student students[], int n) {
int i, j;
for (i = 0; i < n; i++) {
int total = 0; // Reset per student so each sum starts clean
for (j = 0; j < SUBJECTS; j++) {
total += students[i].marks[j]; // Accumulate marks across every subject
}
students[i].total = total;
students[i].percentage = (total / (float)(SUBJECTS * 100)) * 100; // Cast to float so the division isn't truncated to an integer
students[i].grade = calculateGrade(students[i].percentage); // Derive the letter grade from the computed percentage
}
}
Example Calculation

Click Run to see what this code prints.

Step 4: Rank Students by Percentage

Ranking means sorting the students by percentage from highest to lowest, then assigning rank 1 to the top student, rank 2 to the next, and so on. A simple bubble sort is more than fast enough for a class-sized list, and it is easy to follow: repeatedly compare neighboring students and swap them if they are in the wrong order.

void assignRanks(Student students[], int n) {
int i, j;
Student temp; // Holds one student during a swap so neither side of the swap is lost
for (i = 0; i < n - 1; i++) { // Outer pass: bubble sort needs n-1 passes to fully sort
for (j = 0; j < n - 1 - i; j++) { // Each pass needs fewer comparisons as the tail settles
if (students[j].percentage < students[j + 1].percentage) { // Swap when out of descending order
temp = students[j];
students[j] = students[j + 1];
students[j + 1] = temp;
}
}
}
for (i = 0; i < n; i++) {
students[i].rank = i + 1; // After sorting, array position directly maps to class rank
}
}

Note that this sort reorders the `students` array itself by percentage, and assigns `rank` based on the new order — so after calling `assignRanks`, `students[0]` is always the top-ranked student in the class, regardless of the order marks were originally entered in.

Step 5: Print Each Report Card

Printing a report card means displaying the student's name, a per-subject breakdown of marks, and the computed total, percentage, grade, and class rank in a clearly labeled block.

void printReportCard(Student s) { // Takes a copy by value since printing never needs to modify the student
int j;
printf("\n========================================\n");
printf("REPORT CARD\n");
printf("========================================\n");
printf("Name: %s\n", s.name);
printf("----------------------------------------\n");
for (j = 0; j < SUBJECTS; j++) {
printf("Subject %d: %d\n", j + 1, s.marks[j]); // One line per subject's mark
}
printf("----------------------------------------\n");
printf("Total : %d / %d\n", s.total, SUBJECTS * 100); // Max possible is subjects times 100 marks each
printf("Percentage: %.2f%%\n", s.percentage);
printf("Grade : %c\n", s.grade);
printf("Class Rank: %d\n", s.rank);
printf("========================================\n");
}

This function takes `Student s` by value (a copy) rather than by pointer, which is perfectly fine here since printing never needs to modify the student's data — it is a good contrast with the Banking System project, where pointers were needed specifically because those functions did need to modify the original data.

Step 6: Tie It Together in main()

`main` asks how many students to process, calls the input, calculation, and ranking functions in order, then loops over the final, ranked array to print every report card.

int main(void) {
Student students[MAX_STUDENTS]; // Fixed-size array sized to the largest class this program supports
int n, i; // n is how many students were actually entered this run
printf("Enter number of students: ");
scanf("%d", &n);
if (n <= 0 || n > MAX_STUDENTS) { // Reject counts that would under- or over-run the students[] array
printf("Please enter a number between 1 and %d.\n", MAX_STUDENTS);
return 1; // Non-zero exit code signals the program ended due to invalid input
}
inputStudents(students, n); // Phase 1: collect names and marks
calculateResults(students, n); // Phase 2: derive total, percentage, and grade for each student
assignRanks(students, n); // Phase 2 continued: sort by percentage and assign class rank
printf("\n\nGenerating report cards for all students...\n");
for (i = 0; i < n; i++) {
printReportCard(students[i]); // Phase 3: print the final, ranked report cards
}
return 0;
}

Complete Code

Here is the full program, ready to compile with `gcc report_card.c -o report_card`.

#include <stdio.h> // printf/scanf for all console input and output
#include <string.h> // Reserved for string handling used alongside the fixed-size name buffer
#define MAX_STUDENTS 50 // Upper bound on how many students one run of the program can hold
#define SUBJECTS 5 // Fixed number of subjects every student is marked on
#define NAME_LEN 50 // Max stored length for a student's name
typedef struct {
char name[NAME_LEN]; // Student's name
int marks[SUBJECTS]; // One mark per subject, in a fixed-size array matching SUBJECTS
int total; // Sum of all subject marks, filled in later by calculateResults
float percentage; // Total expressed as a percentage of the maximum possible marks
char grade; // Letter grade derived from percentage
int rank; // Class rank, filled in later by assignRanks
} Student;
void inputStudents(Student students[], int n) {
int i, j; // i indexes students, j indexes subjects within each student
for (i = 0; i < n; i++) {
printf("\n--- Student %d ---\n", i + 1);
printf("Enter name: ");
scanf(" %49[^\n]", students[i].name);
for (j = 0; j < SUBJECTS; j++) {
int m; // Local scratch variable so we can validate before storing into marks[j]
do {
printf("Enter marks for subject %d (0-100): ", j + 1);
scanf("%d", &m);
if (m < 0 || m > 100) { // Reject impossible scores outside the valid range
printf("Invalid marks, please enter a value between 0 and 100.\n");
}
} while (m < 0 || m > 100); // Keep re-asking until a valid mark is entered
students[i].marks[j] = m; // Only store once the value has passed validation
}
}
}
char calculateGrade(float percentage) { // Threshold table mapping percentage to a letter grade
if (percentage >= 90) return 'A';
if (percentage >= 75) return 'B';
if (percentage >= 60) return 'C';
if (percentage >= 40) return 'D';
return 'F'; // Anything below the lowest threshold fails
}
void calculateResults(Student students[], int n) {
int i, j;
for (i = 0; i < n; i++) {
int total = 0; // Reset per student so each sum starts clean
for (j = 0; j < SUBJECTS; j++) {
total += students[i].marks[j]; // Accumulate marks across every subject
}
students[i].total = total;
students[i].percentage = (total / (float)(SUBJECTS * 100)) * 100; // Cast to float so the division isn't truncated to an integer
students[i].grade = calculateGrade(students[i].percentage); // Derive the letter grade from the computed percentage
}
}
void assignRanks(Student students[], int n) {
int i, j;
Student temp; // Holds one student during a swap so neither side of the swap is lost
for (i = 0; i < n - 1; i++) { // Outer pass: bubble sort needs n-1 passes to fully sort
for (j = 0; j < n - 1 - i; j++) { // Each pass needs fewer comparisons as the tail settles
if (students[j].percentage < students[j + 1].percentage) { // Swap when out of descending order
temp = students[j];
students[j] = students[j + 1];
students[j + 1] = temp;
}
}
}
for (i = 0; i < n; i++) {
students[i].rank = i + 1; // After sorting, array position directly maps to class rank
}
}
void printReportCard(Student s) { // Takes a copy by value since printing never needs to modify the student
int j;
printf("\n========================================\n");
printf("REPORT CARD\n");
printf("========================================\n");
printf("Name: %s\n", s.name);
printf("----------------------------------------\n");
for (j = 0; j < SUBJECTS; j++) {
printf("Subject %d: %d\n", j + 1, s.marks[j]); // One line per subject's mark
}
printf("----------------------------------------\n");
printf("Total : %d / %d\n", s.total, SUBJECTS * 100); // Max possible is subjects times 100 marks each
printf("Percentage: %.2f%%\n", s.percentage);
printf("Grade : %c\n", s.grade);
printf("Class Rank: %d\n", s.rank);
printf("========================================\n");
}
int main(void) {
Student students[MAX_STUDENTS]; // Fixed-size array sized to the largest class this program supports
int n, i; // n is how many students were actually entered this run
printf("Enter number of students: ");
scanf("%d", &n);
if (n <= 0 || n > MAX_STUDENTS) { // Reject counts that would under- or over-run the students[] array
printf("Please enter a number between 1 and %d.\n", MAX_STUDENTS);
return 1; // Non-zero exit code signals the program ended due to invalid input
}
inputStudents(students, n); // Phase 1: collect names and marks
calculateResults(students, n); // Phase 2: derive total, percentage, and grade for each student
assignRanks(students, n); // Phase 2 continued: sort by percentage and assign class rank
printf("\n\nGenerating report cards for all students...\n");
for (i = 0; i < n; i++) {
printReportCard(students[i]); // Phase 3: print the final, ranked report cards
}
return 0;
}

Sample Run

Sample Run

Click Run to see what this code prints.

Extend This Project

  • Let the user name each subject (e.g. "Math", "Science") instead of using "Subject 1", "Subject 2", and print those names on the report card.
  • Add per-subject pass/fail detection (e.g. below 33 in any single subject fails that subject) and print a "Subjects Failed" line.
  • Save all report cards to a text file using `fopen("report_cards.txt", "w")` so they can be printed or emailed later.
  • Calculate and display class-wide statistics: highest percentage, lowest percentage, and class average.
  • Support weighted subjects (e.g. a lab subject worth 50 marks instead of 100) by storing a `maxMarks` array alongside `marks` and adjusting the percentage formula.

Summary

You built a complete report card generator that reads marks for a whole class, calculates totals, percentages, and letter grades, ranks every student by performance, and prints a clean, formatted report card for each one. Separating input, calculation, ranking, and output into distinct functions — each operating on the same array of structs — is a pattern you will reuse constantly once you move on to larger, real-world C programs.