malloc error when trying to read a maze text file in C [closed]

烈酒焚心 提交于 2019-12-02 13:38:58

You have no way to know whether createMaze succeeds or fails because you fail to validate that your file is actually open for reading. While you save the return of fscanf, you fail to validate in any way that 2 conversions actually took place.

Using fscanf is fine for the first row, but understand that the '\n' is left in stdin and you must account for that before your next read. (your next read being fgetc -- it will happily take '\n' as the next value in the file and assign it to cMaze[0][0].

A 2D array is not equivalent to char **. If you plan on storing the lines that make up the maze and referencing them through char **charRow;, then you need to allocate maze.rows number of pointers to char, and you then need to allocate storage for each line and assign the beginning address for that block of storage to each maze.charRow[x]. (for line oriented input, fgets is a better choice for reading each line)

You allocate for newMaze, but do not assign any values.

Rather than fixing your existing code for you, let me provide an example that properly validates each step you need to take, stores each line in maze.line[x] (your renamed charRow), outputs the maze using the stored values and then frees all memory allocated (each line + the pointers) before exiting. Each individual validation is described in the comments, in-line, below, e.g.

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

#ifndef BUF_SIZ
#define BUF_SIZ 8192
#endif

typedef struct {
    int cols, rows;
    char **line;
} maze_t;

void *xcalloc (size_t nmemb, size_t sz);

int main (int argc, char **argv) {

    char buf[BUF_SIZ] = "";
    int n = 0;
    maze_t maze;
    FILE *fp = argc > 1 ? fopen (argv[1], "r") : stdin;

    if (!fp) {  /* validate file open for reading */
        fprintf (stderr, "error: file open failed '%s'.\n", argv[1]);
        return 1;
    }

    /* read maze.rows & maze.cols */
    if (fscanf (fp, "%d %d", &maze.rows, &maze.cols) != 2) {
        fprintf (stderr, "error: failed to read rows & cols.\n");
        return 1;
    }
    fgets (buf, BUF_SIZ, fp);   /* read discard any additional chars */

    /* allocate/validate maze.rows pointers */
    maze.line = xcalloc (maze.rows, sizeof *maze.line);

    /* read each remaining line up to maze.rows lines */
    while (n < maze.rows && fgets (buf, BUF_SIZ, fp)) {
        size_t len = strlen (buf);      /* get buf length */
        if (len && buf[len-1] == '\n')  /* validate last char is '\n' */
            buf[--len] = 0;             /* overwrite with nul-character */
        else {      /* line too long, handle error */
            fprintf (stderr, "error: line exceeds BUF_SIZ.\n");
            return 1;
        }
        if (len != (size_t)maze.cols) { /* validate maze.cols chars read */
            fprintf (stderr, "error: line exceeds maze.cols.\n");
            return 1;
        }

        /* allocate/validate maze.cols +1 chars */
        maze.line[n] = xcalloc (len + 1, sizeof *maze.line[n]);
        strcpy (maze.line[n++], buf);   /* copy buf to maze.line[n] */
    }

    if (fp != stdin) fclose (fp);   /* close file if not stdin */

    if (n != maze.rows) {   /* validate maze.rows lines read */
        fprintf (stderr, "error: less than maze.rows lines read.\n");
        return 1;
    }

    for (int i = 0; i < n; i++) {
        printf ("%s\n", maze.line[i]);  /* output each line */
        free (maze.line[i]);            /* free line */
    }
    free (maze.line);                   /* free pointers */

    return 0;
}

/** xcalloc allocates memory using calloc and validates the return.
 *  xcalloc allocates memory and reports an error if the value is
 *  null, returning a memory address only if the value is nonzero
 *  freeing the caller of validating within the body of code.
 */
void *xcalloc (size_t nmemb, size_t sz)
{
    register void *memptr = calloc (nmemb, sz);
    if (memptr == 0) {
        perror ("xcalloc() error: virtual memory exhausted.");
        exit (EXIT_FAILURE);
    }

    return memptr;
}

Hopefully this will provide a working example of each of the steps you need to correct in your code. (note: the xcalloc function is just for convenience above to keep from duplicating the validations in the body of the code)

Example Use/Output

$ ./bin/maze <dat/maze.txt
%%%%%
S % %
% % %
%   E
%%%%%

Memory Use/Error Check

In any code you write that dynamically allocates memory, you have 2 responsibilities regarding any block of memory allocated: (1) always preserve a pointer to the starting address for the block of memory so, (2) it can be freed when it is no longer needed.

For Linux valgrind is the normal choice. There are similar memory checkers for every platform. They are all simple to use, just run your program through it.

$ valgrind ./bin/maze <dat/maze.txt
==18822== Memcheck, a memory error detector
==18822== Copyright (C) 2002-2015, and GNU GPL'd, by Julian Seward et al.
==18822== Using Valgrind-3.11.0 and LibVEX; rerun with -h for copyright info
==18822== Command: ./bin/maze
==18822==
%%%%%
S % %
% % %
%   E
%%%%%
==18822==
==18822== HEAP SUMMARY:
==18822==     in use at exit: 0 bytes in 0 blocks
==18822==   total heap usage: 6 allocs, 6 frees, 70 bytes allocated
==18822==
==18822== All heap blocks were freed -- no leaks are possible
==18822==
==18822== For counts of detected and suppressed errors, rerun with: -v
==18822== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)

Always confirm that you have freed all memory you have allocated and that there are no memory errors.

Look things over, work to incorporate the validations in your code and let me know if you have any further questions.

I think your problem is this line:

k = fscanf(pf, "%i %*c %i", &maze.cols, &maze.rows);

With the input

5 5

you'll not get maze.rows initialized. The %*c will "eat" (supress) the second 5.

Try this to debug your code:

k = 0;
maze.cols = 0;
maze.rows = 0;
k = fscanf(pf, "%i %*c %i", &maze.cols, &maze.rows);
printf("%d %d %d\n", k, maze.cols, maze.rows);

and check the values printed. I get the values:

1 5 0

so maze.rows wasn't assigned by the fscanf

After that try this instead:

k = fscanf(pf, "%i %i", &maze.cols, &maze.rows);

You need to read properly row and column numbers like below.

k = fscanf(pf, "%d %d", &maze.cols, &maze.rows);

Also, your for statement has some mistakes. It must be like this.

for (i = 0; i <= maze.rows; i++) {
    for (j = 0; j <= maze.cols; j++) {
        cMaze[i][j] = fgetc(pf);
        putchar(cMaze[i][j]);
    }
}

Edit: Try the code I wrote for your createMaze. If you don't understand feel free to ask more questions.

struct maze_t * createMaze(char * fileName)
{
    typedef struct Maze {
        int cols, rows;
        char **charRow;
    }MAZE;
    struct Maze maze;
    FILE *pf;
    int i, j, k;
    pf = fopen(fileName, "r");
    k = fscanf(pf, "%d %d", &maze.cols, &maze.rows);
    int *newMaze = (int *)malloc(maze.rows * maze.cols * sizeof(int));
    for (i = 0; i <= maze.rows; i++) {
        for (j = 0; j <= maze.cols; j++) {
            newMaze[i+j] = fgetc(pf);
            putchar(newMaze[i+j]);
        }
    }
    printf("\n");
    fclose(pf);

    return newMaze;
}
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!