csegmentation-fault

C Seg Fault and Core Dump when trying to free pointer and close file


I am trying to read from a file to make a linked list of movies. At the end of the read file function when I try and free the line pointer and close the file I get a seg fault and core dump. Both the freeing and file closing are causing problems. Do I need any libraries? the fopen works just fine.

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

struct movie {
    char *title;
    char **languages;
    int year;
    double rating;
    struct movie *next;
};

struct movie *read_file(const char *filename);
struct movie *create_movie(const char *title, const char **languages, int year, double rating);
void append_movie(struct movie *head, struct movie *new_movie);
void free_movie(struct movie *movie);

int main() {
    const char *filename = "movies_sample_1.csv";
    struct movie *head = read_file(filename);

    while (head != NULL) {
        struct movie *temp = head;
        head = head->next;
        free_movie(temp);
    }

    return 0;
}

struct movie *read_file(const char *filename) {
    FILE *file = fopen(filename, "r");
    if (file == NULL) {
        perror("Error opening file");
        exit(1);
    }

    struct movie *head = NULL;
    char *line = NULL;
    size_t len = 0;
    ssize_t nread;

    if ((nread = getline(&line, &len, file)) == -1) {
        perror("Error reading header");
        exit(1);
    }

    while ((nread = getline(&line, &len, file)) != -1) {
        char *title = strtok_r(line, ",", &line);
        char *year_str = strtok_r(NULL, ",", &line);
        char *languages_str = strtok_r(NULL, ",", &line);
        char *rating_str = strtok_r(NULL, ",", &line);


        int year = atoi(year_str);
        double rating = atof(rating_str);

        const char *languages[10];
        int language_count = 0;
        char *language = strtok_r(languages_str, ";[]", &languages_str);

        while (language != NULL) {
            languages[language_count++] = language;
            language = strtok_r(NULL, ";[]", &languages_str);
        }

        languages[language_count] = NULL;

        struct movie *new_movie = create_movie(title, languages, year, rating);

        if (head == NULL) {
            head = new_movie;
        } else {
            append_movie(head, new_movie);
        }
    }

    // if (line != NULL) {
    //     free(line);
    // }

    //fclose(file);
    return head;
}

struct movie *create_movie(const char *title, const char **languages, int year, double rating) {
    struct movie *new_movie = (struct movie *)malloc(sizeof(struct movie));
    if (new_movie == NULL) {
        printf("Memory allocation failed");
        exit(1);
    }

    new_movie->title = strdup(title);
    if (new_movie->title == NULL) {
        printf("Memory allocation failed");
        exit(1);
    }

    new_movie->year = year;
    new_movie->rating = rating;

    int num_languages = 0;
    while (languages[num_languages] != NULL) {
        num_languages++;
    }

    new_movie->languages = (char **)malloc(sizeof(char *) * (num_languages + 1));
    if (new_movie->languages == NULL) {
        printf("Memory allocation failed");
        exit(1);
    }

    for (int i = 0; i < num_languages; i++) {
        new_movie->languages[i] = strdup(languages[i]);
        if (new_movie->languages[i] == NULL) {
            printf("Memory allocation failed");
            exit(1);
        }
    }

    new_movie->languages[num_languages] = NULL;
    new_movie->next = NULL;
    return new_movie;
}

void append_movie(struct movie *head, struct movie *new_movie) {
    struct movie *current = head;
    while (current->next != NULL) {
        current = current->next;
    }
    current->next = new_movie;
}

void free_movie(struct movie *movie) {
    free(movie->title);

    for (int i = 0; movie->languages[i] != NULL; i++) {
        free(movie->languages[i]);
    }

    free(movie->languages);
    free(movie);
}

Solution

  • There may be more problems but here is at least one...

    getline will allocate dynamic memory for line. You must free that memory later by calling free(line). In order to do that you must make sure that line isn't changed, i.e. the value line got from getline is the value to pass to free.

    However, you do

        char *title = strtok_r(line, ",", &line);
        char *year_str = strtok_r(NULL, ",", &line);
        char *languages_str = strtok_r(NULL, ",", &line);
        char *rating_str = strtok_r(NULL, ",", &line);
    

    so the value of line may/will be changed and free(line) will (most likely) fail.

    Solution: Use another char pointer for strtok_r state.