Home > Software engineering >  Realloc produces undefined behavior in C when used with a 2d array
Realloc produces undefined behavior in C when used with a 2d array

Time:08-15

field *field_pointer;

/* Enumerates the whole field by saving the coordinates of the free pieces and the dimensions */
void enum_field(char *filename) {
    FILE *maze;
    maze = fopen(filename, "r");
    int i = 0, j = 0, chr;
    field_pointer = (field *)malloc(sizeof(field *));

    field_pointer->empty_coords = (int **)malloc(sizeof(int *));
    field_pointer->free_spaces = 0;

    while ((chr = fgetc(maze)) != EOF) {
        if (chr == '\n') {
            i = 0;
            j  ;
        } else {
            if (chr != '#') {
                field_pointer->empty_coords[field_pointer->free_spaces] = (int *)malloc(2 * sizeof(int));
                field_pointer->empty_coords[field_pointer->free_spaces][0] = i;
                field_pointer->empty_coords[field_pointer->free_spaces][1] = j;
                field_pointer->free_spaces  ;
                // I believe the error is here V
                field_pointer->empty_coords = (int **)realloc(field_pointer->empty_coords,
                                                              (field_pointer->free_spaces   1) * sizeof(int *));

                if (chr == 'E') {
                    field_pointer->E[0] = i;
                    field_pointer->E[1] = j;
                } else if (chr == 'S') {
                    field_pointer->S[0] = i;
                    field_pointer->S[1] = j;
                }
            }
            i  ;
        }
    }
    field_pointer->x = i;
    field_pointer->y = j;

    fclose(maze);
}

Well, so I have this C function here, which helps me enumerate a 2D ASCII maze so I can then turn it into a graph. The ASCII maze is denoted by # as walls, free spaces as walkable road and S as Start and E as Exit. The function doesn't work and throws a 0xC0000374 which probably means corrupted heap. I tried to investigate further and noticed that most likely, the realloc function which is supposed to enlarge the two-dimensional array is at fault. I am relatively new to C, and have been staring a lot at my code, can you help me with another POV and fresh eyes as to what I might be doing wrong? Here is the layout of the field struct as well:

typedef struct field {
    int x, y, free_spaces;
    int **empty_coords;
    int S[2];
    int E[2];
} field;

CodePudding user response:

There is definitely a sizing problem in your initial allocation:

field_pointer = (field *)malloc(sizeof(field *)) should allocate the size of a structure, not that of a pointer. Use field_pointer = malloc(sizeof(field)) or better:

    field_pointer = malloc(sizeof(*field_pointer));

Note that you could simplify the code by using a real 2D array instead of a pointer to an array of pointers to arrays of 2 int.

Also note that there is no need to manipulate the global object until it is fully loaded. Using local variables is recommended. Returning the structure pointer allows for the caller to detect failure to load the maze. It should be the caller responsibility to store the pointer to the global variable is needed.

Here is a modified version:

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

typedef struct field {
    int x, y, free_spaces;
    int (*empty_coords)[2];
    int S[2];
    int E[2];
} field;

field *field_pointer;

/* Enumerates the whole field by saving the coordinates of the free pieces and the dimensions */
field *enum_field(const char *filename) {
    FILE *maze = fopen(filename, "r");
    if (maze == NULL)
        return NULL;
    field *fp = calloc(sizeof(*fp), 1);
    if (fp == NULL) {
        fclose(maze);
        return NULL;
    }
    fp->free_spaces = 0;
    fp->empty_coords = NULL;
    int i = 0, j = 0, chr;

    while ((chr = fgetc(maze)) != EOF) {
        if (chr == '\n') {
            if (fp->x < i)
                fp->x = i;
            i = 0;
            j  ;
        } else {
            if (chr != '#') {
                // allocate space for one extra set of coordinates
                int (*new_coords)[2] = realloc(fp->empty_coords,
                                               (fp->free_spaces   1) * sizeof(*fp->empty_coords));
                if (!new_coords) {
                    free(fp->empty_coords);
                    free(fp);
                    fclose(maze);
                    return NULL;
                }
                fp = new_coords;
                fp->empty_coords[fp->free_spaces][0] = i;
                fp->empty_coords[fp->free_spaces][1] = j;
                fp->free_spaces  ;

                if (chr == 'E') {
                    fp->E[0] = i;
                    fp->E[1] = j;
                } else
                if (chr == 'S') {
                    fp->S[0] = i;
                    fp->S[1] = j;
                }
            }
            i  ;
        }
    }
    fp->y = j;
    fclose(maze);
    // return structure pointer for the caller to store to into field_pointer
    return fp;
}
  • Related