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