Hello, OnlineGDB Q&A section lets you put your programming query to fellow community users. Asking a solution for whole assignment is strictly not allowed. You may ask for help where you are stuck. Try to add as much information as possible so that fellow users can know about your problem statement easily.

Include the average of the test scores, i tried to but its only printing one score and no average

+2 votes
asked Nov 30, 2023 by Andre Davis (320 points)

Calculates the average score for each student and stores this
information, from a dynamic allocation

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

typedef struct {
    char name[50];
    int testScores [5];
    double average;
} Student;

Student* addStudent(Student* table, int* tableSize);
void viewTable(Student* table, int tableSize);

int main()
{
    Student* table = NULL;
    int tableSize = 0;
    int choice;
    
    while (1) {
        printf("\n Student Grading Menu: \n");
        printf("1. Add a student to the system\n");
        printf("2. View grading table\n");
        printf("3. Exit\n");
        printf("Enter your choice: ");
        scanf("%d", &choice);
        
        switch (choice) {
            case 1: 
                table = addStudent(table, &tableSize);
                break;
            case 2:
                viewTable(table, tableSize);
                break;
            case 3:
                free(table);
                exit(0);
            default:
                printf("Invalid choice. Please enter a valid option.\n");
        }
    }
    
    return 0;
}

Student* addStudent(Student* table, int* tableSize){
    (*tableSize)++;
    table = (Student*)realloc(table, (*tableSize) * sizeof(Student));
    
    if(table == NULL) {
        printf("Memory reallocation failed. Product not added.\n");
        (*tableSize)--;    
    } else {
        Student* newStudent = &table[(*tableSize) - 1];
        printf("Enter the student's name: ");
        scanf("%s", newStudent->name);
        printf("Enter the test score: ");
        for (int i = 0; i < 5; i++){
        scanf("%d", &newStudent->testScores[i]);
        }
        printf("Student added to the table.\n");
    }
    
    return table;
}
    void viewTable(Student* table, int tableSize){
        if(tableSize == 0) {
            printf("Table is empty. \n");
        } 
        printf("Student Table:\n");
        for (int i = 0; i < tableSize; i++) {
            Student* student = &table[i];
            double average = student->testScores[i] / sizeof(Student);
            printf("Student: %s, Grade: %d, %d, %d, %d, %d\n Grade Average %lf", student->name, student->testScores[i], student-> average);
}
            
}

1 Answer

0 votes
answered Dec 5, 2023 by Peter Minarik (86,240 points)

Hi Andre,

I've looked at your code and fixed it up in a way that made sense to me. Check if this is what you need.

Let me highlight some of the changes.

Average not shown

This happens, because of your code:

printf("Student: %s, Grade: %d, %d, %d, %d, %d\n Grade Average %lf", student->name, student->testScores[i], student-> average);

Above, you output 7 variables (%s, 5 x %d, %lf), but you provided only 3 variables: student->name, student->testScores[i] (this is just just one variable), average (you must not have the student-> before it).

Please, see my suggested calculation for this. Also, I'm not sure if you want the average grade of the student across the 5 subjects (what I did) or if you want the average grade per subject across each student.

Address change in function

In addStudent(), you try to change the address of the student table. The table is of type Student*, so the address that stores this data is Student**. To be able to change the address of your student table you need to pass in the address of your student table (i.e. Student**). Instead, what you do is you pass addStudent() the table itself (which is also a memory address, but just Student*, since every array is an address where a sequence of similar elements starts) and call it table. This will be a local variable and you keep changing this one, reallocate this local variable, and return this local variable. However the scope of the local variable is limited to the scope of the function, so after leaving the function, the memory may be released, reused, etc, so you cannot rely on it being available outside of its scope.

For this, the right approach is to get the address of the original variable (the table) and change the address of that one, not a local variable. It may be a bit confusing at first, but I hope it will make sense in time.

Improvements

I left some comments for improvement opportunities.
One of them is when you scan for the choice if the user enters anything non-numeric (e.g. the letter 'a') then your code ends up in an infinite loop. If you want to deal with this corner case, you'd need to read a string first, then parse the numbers from it.
Another improvement could be reading the name as it has the same problem, at the first white space it stops reading, so it couldn't accept names like "John Doe". fgets(buffer, size, stdin) would be useful here
char name[50];
fgets(name, sizeof(name), stdin);
but fgets and scanf do not always play nice together in the same code. But that's a different topic. :)

Bits and bobs

I changed a few things here and there. Mostly cosmetics and just better user experience (in my mind at least).

I removed the #define _CRT_SECURE_NO_WARNINGS, as I do not see why you'd need them (unless on Windows it's causing problems, I only checked the code in Online GDB -- Linux).

Final code

And here's the code with my proposed changes:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

typedef struct
{
    char name[50];
    int testScores [5];
    double average;
} Student;

void addStudent(Student** table, int* tableSize);
void viewTable(Student* table, int tableSize);

int main()
{
    Student* table = NULL;
    int tableSize = 0;
    int choice;
    
    while (1)
    {
        printf("\nStudent Grading Menu:\n");
        printf("=====================\n");
        printf("    1. Add a student to the system\n");
        printf("    2. View grading table\n");
        printf("    3. Exit\n\n");
        printf("Enter your choice (1-3): ");
        scanf("%d", &choice); // PM: You could try to make this smarter by handling non-numbers
        
        switch (choice)
        {
            case 1: 
                addStudent(&table, &tableSize);
                break;
            case 2:
                viewTable(table, tableSize);
                break;
            case 3:
                free(table);
                exit(0);
            default:
                printf("Invalid choice. Please enter a valid option.\n");
        }
    }
    
    return 0;
}

void addStudent(Student** table, int* tableSize)
{
    (*tableSize)++;
    *table = (Student*)realloc(*table, (*tableSize) * sizeof(Student));
    
    if (table == NULL)
    {
        printf("Memory reallocation failed. Product not added.\n");
        (*tableSize)--;
        // PM: This is wrong. You lost your pointer to the table, the size doesn't matter anymore if your data is lost.
        // You either:
        //     - exit after signalling the error
        //     - (better solution) if you expect that you may run out of memory, then do not reallocate, just allocate a new pointer and if it was successfull, `table` should point at it (after the old table is freed)
        return;
    }

    Student* newStudent = &(*table)[(*tableSize) - 1];
    printf("Enter the student's name: ");
    scanf("%s", newStudent->name); // PM: This does not allow SPACE in the name, so no names with regular [first name][SPACE][last name] format are accepted.
    printf("Enter the test scores:\n");
    for (int i = 0; i < 5; i++)
    {
        printf("    score %d: ", i + 1);
        scanf("%d", &newStudent->testScores[i]);
    }
    printf("Student added to the table.\n");
}

void viewTable(Student* table, int tableSize)
{
    printf("\nStudent Table:\n");
    printf("--------------\n");

    if (tableSize == 0)
    {
        printf("[Table is empty.]\n");
    } 
    
    for (int i = 0; i < tableSize; i++)
    {
        Student* student = &table[i];
        printf("Student Name: %s\n", student->name);
        printf("        Grades: ");

        double average = 0.0;
        for (int j = 0; j < 5; j++)
        {
            average += student->testScores[j];
            if (j > 0)
            {
                printf(", ");
            }
            printf("%d", student->testScores[j]);
        }
        average /= 5.0;
        printf("\n        Grade Average: %lf\n", average);
    }
}

I hope this helps. Good luck! :)

Welcome to OnlineGDB Q&A, where you can ask questions related to programming and OnlineGDB IDE and and receive answers from other members of the community.
...