Swagnoor Singh
Swagnoor Singh

Reputation: 23

Why does this code keep giving me a Segfault Error when i run it in Linux?

So I'm trying to create a function that takes in a text file, which contains a bunch of words separated by the newline character, and reads the text file into a char** array.

When I run this code in netbeans on windows, it works fine but if I run it in Linux, I get a segmentation fault error.

// globals
FILE *words_file;
char **dic;
int num_words = 0;

void read_to_array() {
    words_file = fopen("words.txt", "r");
    char *line = NULL;
    int i = 0;
    size_t len = 0;
    dic = (char **)malloc(99999 * sizeof(char *));

    // read dic to array
    while (getline(&line, &len, words_file) != -1) {
        dic[i] = (char*)malloc(len);
        strcpy(dic[i], line);

        // get rid of \n after word
        if (dic[i][strlen(dic[i]) - 1] == '\n') {
            dic[i][strlen(dic[i]) - 1] = '\0';
        }
        ++i; 
        num_words++;
    }
    //printf("%s", dic[i][strlen(dic[i]) - 1]);  //testing 
    fclose(words_file);
    dic[i] = NULL;
}

What am I missing here?

Upvotes: 1

Views: 140

Answers (1)

chqrlie
chqrlie

Reputation: 144770

There are some problems in your program that may cause the undefined behavior that you observe:

  • You do not test if the file was open successfully, causing undefined behavior if the file is not where you expect it or has a different name.
  • You do not limit the number of lines read into the array, causing undefined behavior if the file contains more than 99998 lines, which may be be the case in linux as /usr/share/dict/words has 139716 lines on my system, for example.

Your memory allocation scheme is suboptimal but correct: you should compute the length of the word and strip the newline before allocating the copy. As coded, you allocate too much memory. Yet you should free line before returning from read_to_array and you should avoid using global variables.

Here is a modified version:

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

char **read_to_array(const char *filename, int *countp) {
    FILE *words_file;
    char *line = NULL;
    size_t line_size = 0;
    char **dic = NULL;
    int dic_size = 0;
    int i = 0;

    words_file = fopen(filename, "r");
    if (words_file == NULL) {
        fprintf(stderr, "cannot open dictionary file %s\n", filename);
        return NULL;
    }

    dic_size = 99999;
    dic = malloc(dic_size * sizeof(char *));
    if (dic == NULL) {
        fprintf(stderr, "cannot allocate dictionary array\n");
        fclose(words_file);
        return NULL;
    }

    // read dic to array
    while (getline(&line, &line_size, words_file) != -1) {
        size_t len = strlen(line);
        /* strip the newline if any */
        if (len > 0 && line[len - 1] == '\n') {
            line[--len] = '\0';
        }
        if (i >= dic_size - 1) {
            /* too many lines: should reallocate the dictionary */
            fprintf(stderr, "too many lines\n");
            break;
        }
        dic[i] = malloc(len + 1);
        if (dic[i] == NULL) {
            /* out of memory: report the error */
            fprintf(stderr, "cannot allocate memory for line %d\n", i);
            break;
        }
        strcpy(dic[i], line);
        i++;
    }
    dic[i] = NULL;
    *countp = i;
    fclose(words_file);
    free(line);
    return dic;
}

int main(int argc, char **argv) {
    const char *filename = (argc > 1) ? argv[1] : "words.txt";
    int num_words;
    char **dic = read_to_array(filename, &num_words);
    if (dic != NULL) {
        printf("dictionary loaded: %d lines\n", num_words);
        while (num_words > 0)
            free(dic[--num_words]);
        free(dic);
    }
    return 0;
}

Output:

chqrlie> readdic /usr/share/dict/words
too many lines
dictionary loaded: 99998 lines

Upvotes: 1

Related Questions