Search code examples
csegmentation-faultstrtoull

C: Convert string to unsigned long long Error


I'm simply trying to convert a String (readed from a line in a file) in a long long variable. My problem is that i get Segmentation Fault, and I don't know why... Here's the code: (I put a comment on the error on the "3° Call" function)

#define CAPACITY (unsigned long long) 11
#define INTEGERS_PATH // the path to the file I wish to read

typedef struct {
    void** array;
    unsigned long long el_num;
    unsigned long long array_capacity;
    int (*compare)(void*,void*);
} GenericArray;

// used for compare elements
struct record{
    char* string_field; // used in case the GenericArray is made of strings items
    long long integer_field; // used in case the GenericArray is made of long long items
};

// END OF STRUCTS ---------------------------------------------------------------------------------
// BEGIN OF FUNCTIONS -----------------------------------------------------------------------------

int main(int argc, char const *argv[]) {
    test_with_comparison_function(compare_record_int_field);
    return (EXIT_SUCCESS);
}

// Compare function passed to generic_array_create() for create the GenericArray
static int compare_record_int_field(void* r1_p,void* r2_p){
    if(r1_p == NULL){
        fprintf(stderr,"compare_record_int_field: the first parameter is a null pointer");
        exit(EXIT_FAILURE);
    }
    if(r2_p == NULL){
        fprintf(stderr,"compare_record_int_field: the second parameter is a null pointer");
        exit(EXIT_FAILURE);
    }

    struct record *rec1_p = (struct record*)r1_p;
    struct record *rec2_p = (struct record*)r2_p;

    if(rec1_p->integer_field < rec2_p->integer_field){
        return(1);
    }
    return(0);
}

// 1° Call
static void test_with_comparison_function(int (*compare)(void*, void*)) {
    GenericArray* array = generic_array_create(compare);
    load_array(array);
    print_array(array); // it prints the array
    free_array(array); // it frees the memory alocated by the array
}

// 2° Call
GenericArray *generic_array_create(int (*compare)(void*,void*)){
    GenericArray *array = malloc(sizeof(GenericArray));
    if(array == NULL){
        fprintf(stderr, "generic_array_create: unable to allocate memory for the generic array");
        exit(EXIT_FAILURE);
    }

    array->array = malloc(CAPACITY * sizeof(void*));
    array->el_num = 0;
    array->array_capacity = CAPACITY;
    array->compare = compare;

    return(array);
}

// 3° Call
static void load_array(GenericArray* array){
    clock_t start = clock(); // for timing

    printf("\nLoading data from file...\n");

    FILE * dataset_p = fopen(INTEGERS_PATH, "r");
    if(dataset_p == NULL){
        fprintf(stderr,"main: unable to open the file");
        exit(EXIT_FAILURE);
    }

    char *read_line_p;
    char buffer[1024];
    int buf_size = 1024;

    while(fgets(buffer, buf_size, dataset_p) != NULL){
        read_line_p = malloc((strlen(buffer) + 1) * sizeof(char));
        strcpy(read_line_p, buffer);
        char *string_field_in_read_line_p = strtok(read_line_p, "\n");
        char *integer_field_in_read_line_p = strtok(NULL, "\n");

        char *string_field_1 = malloc((strlen(string_field_in_read_line_p) + 1) * sizeof(char));
        char *string_field_2 = malloc((strlen(string_field_in_read_line_p) + 1) * sizeof(char));

        strcpy(string_field_1,string_field_in_read_line_p);
        strcpy(string_field_2,string_field_in_read_line_p);

        /* Here begins errors (SEGMENTATION FAULT) */
        int integer_field = atoi(integer_field_in_read_line_p); 
        long long integer_field = strtoll(integer_field_in_read_line_p, (char **) NULL, 10);

        struct record *record_p = malloc(sizeof(struct record));
        record_p->string_field = string_field_1;
        record_p->integer_field = integer_field;

        generic_array_add(array, (void*) record_p);

        free(read_line_p); 
    }

    fclose(dataset_p);
    printf("\nData loaded\n");
}

Here's the file which I want to read, it's a simple .txt file:

9
8
7
6
5
4
3
2
2
1
10

Solution

  • The problem is these three lines:

    while(fgets(buffer, buf_size, dataset_p) != NULL){
        // ...
        char *string_field_in_read_line_p = strtok(read_line_p, "\n");
        char *integer_field_in_read_line_p = strtok(NULL, "\n");
    

    The fgets function reads one line, and leaves the newline '\n' at the end of that. That means there's only one '\n' in the string you attempt to tokenize. So the second call to strtok should return NULL.

    You don't check for that, and therefore any dereference of that NULL pointer (which happens in the conversion functions) will lead to undefined behavior and possible crashes.