Martin Lopez
Martin Lopez

Reputation: 103

pointers and Structs from csv file

I need to read-in a .csv file and print it's contents, the students' names and age. The actual output is correct but it is appended with junk characters. The program also terminates with a segmentation fault! For some reason, these problems do not occur on OSX but do on Windows.

Code so far:

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

#define MAX_LINE_LENGTH 80     
#define MAX_NUM_STUDENTS 500   
#define MAX_NAME_SIZE 50       


typedef struct student_s Student;

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


Student studentPool[MAX_NUM_STUDENTS];  // The student pool
int firstFree = 0;

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;
}

Student* readOneStudent(FILE* file)
{
    char buffer[MAX_LINE_LENGTH];  
    Student* student = NULL;      

    char* inputLine = fgets(buffer, MAX_LINE_LENGTH, file);
    if (inputLine != 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;
}

int precedes(const Student* new, const Student* old)
{
    int final = strcmp(old->name, new->name);
    if (final == 0) {
        if (old->age <= new->age) {
            final = -1;
        } else {
            final = 1;
        }
    }
    return final;
}

Student* insert(Student* student, Student* list)
{
    Student* current = list;
    if (list == NULL) {
        student->next = list;
        list = student;
    } else if (precedes(current, student) < 0) {
        student->next = current;
        current = student;
        list = current;
    } else {
        while(current->next && precedes(student, current->next) < 0) {
            current = current->next;
        }
        student->next = current->next;
        current->next = student;
    }
    return list;
}

Student* readStudents(FILE *file)
{
    Student* student_list;
    Student* student = readOneStudent(file);
    while (student != NULL) {
        student_list = insert(student, student_list);
        student = readOneStudent(file);
    }
    return student_list;
}

void printOneStudent(Student student)
{
    printf("%s (%d)\n", student.name, student.age);
}

void printStudents(const Student* student)
{
    while (student != NULL) {
        printOneStudent(*student);
        student = student->next;
    }
}

int main(void)
{
    FILE* inputFile = fopen("studlist.txt", "r");
    if (inputFile == NULL) {
        fprintf(stderr, "File not found\n");
    } else {
        Student* studentList = readStudents(inputFile);
        printStudents(studentList);

    }
} 

input:

Zaphod Beeblebrox,250

Albert Einstein,133

Albert Einstein,7

The output is sorted alphabetically for student names' then increasing age.
expected output:

Albert Einstein (7)

Albert Einstein (133)

Zaphod Beeblebrox (250)

on Windows, output is appended with:

p @ (0)

then segfault.

Upvotes: 2

Views: 88

Answers (1)

igon
igon

Reputation: 3046

student_list is a uninitialized stack variable. It could be 0 (NULL) on some machines but that does not have to be true. In general you should expect an initialized stack variable to contain whatever garbage is in that location of memory.

Here is the simple fix:

Student* readStudents(FILE *file)
{
    Student* student_list = NULL;
    Student* student = readOneStudent(file);
    while (student != NULL) {
        student_list = insert(student, student_list);
        student = readOneStudent(file);
    }
    return student_list;
}

The bug is triggered in the insertion method here:

if (list == NULL) {
    student->next = list;
    list = student;
}

Whatever the variable list is pointing to will be used for sorting. In your case it looks like it was "bigger" than all the legit inputs and garbage made its way to the back of your linked list. The print function would then keep dereferencing uninitialized next pointers until the SIGSEGV is triggered.

For debugging this sort of problems you might want to check out valgrind.

Upvotes: 3

Related Questions