user99point9
user99point9

Reputation: 117

Changing calls within a Struct

Given the struct I have created below, how would I change the code to make it read from insert (student) rather than from the way it is now. Basically, the code at the moment takes input as a student written as:

student guy,23

and the output will be:

student guy (23)

and this part is working thus far. What the insert (and comes before) code will do is when multiple students are entered, sort them in alphabetical order - or if they have an identical name, by age. I have completed the code to do this but cannot seem to work out how to call it properly.

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <stdbool.h>
#include <assert.h>

#define MAX_LINE_LENGTH 80      // The longest line this program will accept
#define MAX_NUM_STUDENTS 500    // The maximum number of students this program can handle
#define MAX_NAME_SIZE 50        // The maximum allowable name length

// The declaration of the student record (or struct). Note that
// the struct contains the name as an array of characters, rather than
// containing just a pointer to the name as before.

typedef struct student_s Student;

struct student_s {
    char name[MAX_NAME_SIZE];
    int age;
    Student* next;              // Pointer to next student in a list
};

bool comesBefore(const Student* student1, const Student* student2) {
    int name_compare = strcmp(student1->name, student2->name);

    if (name_compare < 0) {
        return true;
    }
    else if (name_compare == 0) {
        int age1 = student1->age;
        int age2 = student2->age;
        if (age1 < age2) {
            return true;
        }
    }
    return false;
}

Student* insert(Student* student, Student* list) {
    Student* curr = NULL;
    Student* prev = NULL;
    if (list == NULL)
        return student;

    if (comesBefore(student, list)) {
        student->next = list;
        return student;
    }

    for (curr = list, prev = NULL;
         curr != NULL && comesBefore(student, curr) != true;
         prev = curr, curr = curr->next);

    assert(prev != NULL);

    student->next = curr;
    prev->next = student;
    return list;
}


// Create a pool of student records to be allocated on demand

Student studentPool[MAX_NUM_STUDENTS];  // The student pool
int firstFree = 0;
// Return a pointer to a new student record from the pool, after
// filling in the provided name and age fields. Returns NULL if
// the student pool is exhausted.
Student* newStudent(const char* name, int age) {
    Student* student = NULL;
    if (firstFree < MAX_NUM_STUDENTS) {
        student = &studentPool[firstFree];
        firstFree += 1;
        strncpy(student->name, name, MAX_NAME_SIZE);
        student->name[MAX_NAME_SIZE - 1] = '\0';  // Make sure it's terminated
        student->age = age;
        student->next = NULL;
    }
    return student;
}

// Read a single student from a csv input file with student name in first column,
// and student age in second.
// Returns: A pointer to a Student record, or NULL if EOF or an invalid
// student record is read. Blank lines, or lines in which the name is
// longer than the provided name buffer, or there is no comma in the line
// are considered invalid.
Student* readOneStudent(FILE* file)
{
    char buffer[MAX_LINE_LENGTH];  // Buffer into which we read a line from stdin
    Student* student = NULL;       // Pointer to a student record from the pool

    // Read a line, extract name and age

    char* cp = fgets(buffer, MAX_LINE_LENGTH, file);
    if (cp != NULL) {           // Proceed only if we read something
        char* commaPos = strchr(buffer, ',');
        if (commaPos != NULL && commaPos > buffer) {
            int age = atoi(commaPos + 1);
            *commaPos = '\0';  // null-terminate the name
            student = newStudent(buffer, age);
        }
    }
    return student;
}

// Reads a list of students from a given file. Input stops when
// a blank line is read, or an EOF occurs, or an illegal input
// line is encountered.
// Returns a pointer to the first student in the list or NULL if no
// valid student records could be read.
Student* readStudents(FILE *file)
{
    Student* first = NULL;     // Pointer to the first student in the list
    Student* last = NULL;      // Pointer to the last student in the list
    Student* student = readOneStudent(file);
    while (student != NULL) {
        if (first == NULL) {
            first = last = student;   // Empty list case
        }
        else {
            last->next = student;
            last = student;
        }
        student= readOneStudent(file);
    }
    return first;
}


// printOneStudent: prints a single student, passed by value
void printOneStudent(Student student)
{
    printf("%s (%d)\n", student.name, student.age);
}


// printStudents: print all students in a list of students, passed
// by reference
void printStudents(const Student* student)
{
    while (student != NULL) {
        printOneStudent(*student);
        student = student->next;
    }
}

// Main program. Read a linked list of students from a csv file, then display
// the contents of that list.
int main(void)
{
    FILE* inputFile = stdin;
    if (inputFile == NULL) {
        fprintf(stderr, "File not found\n");
    }
    else {
        Student* studentList = readStudents(inputFile);
        printStudents(studentList);

        // The program could now do various things that make use of
        // the linked list, like deleting students and adding new ones,
        // but the program is already quite long enough!
    }
}

I believe it requires some sort of edit to readStudents but cannot work out what call change to make regardless of source material I have read.

Upvotes: 1

Views: 64

Answers (1)

djna
djna

Reputation: 55957

The key idea is that you need to adjust this:

    if (first == NULL) {
        first = last = student;   // Empty list case
    }
    else {
        last->next = student;
        last = student;
    }

At present you are putting the new student at the end of the list. Instead you are going to keep the list in a sorted order. In concept you have

 Arthur -> Bill -> Dave

and a new record, Charles. So you compare first with Charles, discover Charles is bigger, so go on to Bill, and then eventually hit Dave and now know to insert Charles after Bill and before Dave.

Now have a look at the insert() function. Can you see that this is what it's doing? Walking the list and eventually inserting the record - note how it adjusts the "Bill" to point to the new record, and have the new record point to Dave.

Upvotes: 2

Related Questions