Learn to create and use a struct
More practice with creating custom types
Practice dynamically allocating arrays
Learn to redirect a file to stdin
For this lab, you will input and store a set of student records. For each student, you will keep track of their student id (sid), their name, and a list of course grades. From this information, you will calculate each student's GPA. After recording all the student information and calculating the GPA, you will print back all the records in your student database.
Begin by defining a struct. In this structure, you will store the following fields:
student id named sid (int)
last name (char array)
first name (char array)
list of grades (float pointer)
gpa value (float)
For the first and last name arrays, assume a maximum size of 25. Set a preprocessor directive #define NAME_SIZE 25 and use this value to determine the size of the first and last name character arrays.
We also wish to store a list of grades in the student record. However, we do not know the size of this array in advance. Instead you should store a pointer to a float. You will later set this pointer to point to the beginning of an array of floating point values (the grades).
Lastly, wrap your structure definition with a type definition and name your type student.
TIP: By using a typedef and naming it student, we can avoid writing out struct student each time we need to refer to the structure, and instead refer to just student.
Prompt the user for the total number of students. Next, prompt the user for the total number of grades to record.
Print back to the user the total number of students and total number of grades.
TIP: Use the sizeof function to determine the size of any type, including custom types.
Using calloc, dynamically allocate memory for an array of students. The parameters will be the total number of students and the size of each element (the student record). Save this result as a pointer to your custom student type. This pointer points to the first student in an array of student records. The array is initially empty, but you will populate it in the next step.
TIP: The calloc and malloc functions return a void pointer. Do not forget to cast this void pointer to be a pointer to a student record (student *).
Next, iterate over the total number of students. For each student, call your helper function to input an individual student's information (described below). This function will return a single student record. Save this student record in the appropriate spot in your array of students. Since we only have a pointer to the start of our array, you will need to use pointer arithmetic to access each array element.
TIP: Since you have a pointer to the start of your array of student records, you can add your loop counter to this address to find the appropriate array offset. Do not forget to dereference in order to set a value at a pointer, e.g.:
*(pointer + offset) = ...
Next, call a helper function which will be responsible for iterating over all student records, printing out each student record one by one.
Lastly, call a helper function to release the memory associated with your array of students and their grades.
Function to Input Student Information
Write a function that is responsible for inputting information pertaining to a single student and storing that information as a single student record. This function will take in the total number of grades (int) to enter as its only argument. This function will return a single student record.
Declare a new student record. It is initially empty.
Prompt the user for the student id (sid) and store it in the proper field in the student record.
Prompt the user for a last name and store it in the student record.
Prompt the user for a first name and store it in the student record.
Using the total number of grades, create a new array of grades in which each grade is a floating point number. Using calloc allocate memory for an array of floating point numbers. Save this pointer in the appropriate spot in your student record.
Prompt the user to enter grades. Using a loop and scanf, input a list of grades. Store each grade in the array of grades.
Calculate the student's GPA using a helper function (described below). Save this value as the gpa field of the student record.
Return the (now populated) student record.
Function to Calculate the GPA
Write a function that takes in a single student record and the total number of grades (int) as arguments. This function will return a floating point value that corresponds to the student's GPA. The students's GPA should be calculated as the average of all of the student's grades.
TIP: To calculate an average you will loop over all grades in the array, calculate the sum, and divide that sum by the total number of grades.
Function to Print all Student Records
Next, write a function that will print out the records of all the students. This function will return nothing. It will take the arguments:
A pointer to the start of your array of students
The total number of students (int)
The total number of grades (int)
TIP: Recall the -> operator is known as a structure dereference. The following two operations are equivalent:
( *(pointer + offset ) ).field
( pointer + offset)->field
You will begin by looping up to the total number of students. For each student:
Print out the student id
Print out the student name in the format: first last
(Note that the space inbetween the names is important)
Print out the student GPA. Format to print two digits after the decimal place.
Print out the student grades all on one line with a space between individual values. Format to print one digit after the decimal place.
Function to Free Memory
Lastly, write a function that will free all of the memory allocated by the program's previous calls to calloc or malloc. This function will return nothing. It will take the arguments:
A pointer to the start of the array of students
The total number of students
This function should loop over the array of students, calling free on the grades float pointer of the student, and then setting the pointer to null. If this step is not done, the program will leak memory, which will lead to performance and out-of-memory problems while the program is running. The following pseudocode illustrates how to accomplish this.
foreach student in student_list:
free( student.grades )
student.grades = null
free( student_list )
student_list = null
Redirecting a File to Standard Input
As you test and debug your program, you notice that there is a lot to type in each time. This can be very tedious. Fortunately, unix gives us a way to plug data from a text file in as standard input to a C program. First, create some data (example below) in a text file and save this file as grades.txt.
111 Bagley Jess 90.5 96.7 89 96 84.2
187 Reynolds Ryder 85.3 88.1 82.3 92.8 79.1
242 Winslow Mckenzie 75.3 82.3 88.1 92.4 78.3
616 Traves Cory 67.2 70.3 45.3 90.1 77.2
The first line gives the total number of students followed by the total number of grades. Each subsequent line gives the infomation for a single student: the sid, the last name, the first name, and 5 grade values. In this example there are 4 students lusted because the first line indicated there would be 4 students. Likewise, there are 5 grade values because the first line indicated there would be 5 grades to record for each student.
TIP: We intend for you to only use scanf in this program, and not the gets function. Recall that scanf will delineate input based on any white space, but that gets uses only the EOL character. For the input data file to work properly as described here, use only scanf in your program.
To use file data directly as stdin to your scanf functions, use the following syntax when executing your program:
$ ./program3 < grades.txt
TIP: When using data from an input file, the values will not be echoed back to the terminal. Therefore some of your formatting may look a bit strange, but that is okay so long as the input data has been properly saved by your code. You can verify you are saving the input data correctly by viewing the output of your print function.
With file input redirection:
[esus] $ ./program3 < grades.txt
Number of students: 4
Number of grades (each): 5
ID: 111, Name: Jess Bagley, GPA: 91.28
Grades: 90.5 96.7 89.0 96.0 84.2
ID: 187, Name: Ryder Reynolds, GPA: 85.52
Grades: 85.3 88.1 82.3 92.8 79.1
ID: 242, Name: Mckenzie Winslow, GPA: 83.28
Grades: 75.3 82.3 88.1 92.4 78.3
ID: 616, Name: Cory Traves, GPA: 70.02
Grades: 67.2 70.3 45.3 90.1 77.2
[esus] $ ./program3
Number of students: 2
Number of grades (each): 3
10 Lincon Becky 93.4 89.2 86.9
3 Stephenson Alfred 89.7 85.3 86.1
ID: 10, Name: Becky Lincon, GPA: 89.83
Grades: 93.4 89.2 86.9
ID: 3, Name: Alfred Stephenson, GPA: 87.03
Grades: 89.7 85.3 86.1
Compile & Test
Compile your program using this gcc command. c99 is a shortcut for running gcc -std=c99, which uses the C99 standard instead of the default C89 standard.
$ c99 -Wall program3.c -o program3
NOTE: Make sure you output what is expected, nothing more and nothing less.
You should attend the Friday Lab session to seek assistance from the TAs and CAs.
For specific questions, attend the weekly lab session or attend the TA's office hours.
You are encouraged to use resources or tutorials on the internet to learn unix or C. Check the class resource list for some links to resources.Attachments