Probably the easy --- and safe --- way to go is to write the code around your data and encapsulate Maze as you could do in any more object-oriented languages.
Below is a complete example, with comments and output, of an alternative way of writing this kind of code.
from the original code
void init(void); int play(void); typedef struct Node { int x, y; } Node; /* global variables */ Node* nodes; int width, height;
In data structures in general when you see a Node thing there is some object around it, an array, a linked list, a map, a set, whathever.
But the node is not the structure and the structure is not a node. A structure is a (possibly empty) collection of nodes. And the nodes points to --- or contains --- data, a unit of data per node.
In your case the structure is a Maze, an array of Nodes. The data is a pair of int.
But you have no struct for a Maze. Failure on doing so takes your representation far from the model itself. This will make the code hard to implement, to test and to change.
You have a single pointer Node*, a global one, and 2 global int. This is fragile. And play relies on a single global pointer and returns nothing: more and more problems.
An alternative: Maze as an object
typedef struct { int x; int y; } Node; typedef struct { unsigned width; unsigned height; Node* nd; } Maze;
This is just an example, and I will left an implementation around this, in order to illustrate the way many people write these things
some operations on Maze
Maze* so_create(unsigned W, unsigned H); Maze* so_destroy(Maze* maze); int so_display(Maze* maze, const char* title); Node* so_get_node(unsigned x, unsigned y, Maze* maze); Maze* so_resize( Maze** original, unsigned new_w, unsigned new_h); // shows a single node int so_show_node(Node* node, const char* msg); // helper: just number all maze nodes int so_fill_maze(Maze* maze); int so_play(Maze* maze); // does nothing here
This way play() could be int so_play(Maze* maze) and you can see one of the differences: it becomes easy to play dozens of mazes on the same code.
A very simple implementation for these functions are below. They are minimal, with 5 to 10 lines each, and serves only as illustration.
so_create(w,h) returns a new Maze with the required size so_destroy() returns NULLand destroys the maze, deallocating the memory so_display() shows a maze on screen so the program can test itself. And a message can be inserted so we will not need to insert dozens of calls to printf() so_get_node returns a new Node with the values of the one in the specified position in the Maze, or NULL so_resize() does the obvious, but here it copies nodes were possible, just for fun and testing so_show_node prints a single node. This is to make life easier if we want to customize the way each node is printed. The version in the example here uses the format (xxx,yyy) so_fill_maze load each Node in Maze with its position, so we can see on screen how things are going. so_play does nothing and it is just as the alternative to void play(void)
main for a test run
This code is self-explanatory but:
- a 2x4 maze is created
- it is displayed we expect all nodes
(0,0) fill_maze is called, numbering all nodes with its position - the
Maze is displayed again - the maze is resized and displayed again
- a node at a certain position is consulted
- the data is
freed
#include "stuff.h" int main(void) { Maze* tst = so_create(2, 4); so_display(tst, "2x4 just created"); so_fill_maze(tst); so_display(tst, "\nsame one, after fill"); // tst is destroyed inside so_resize() Maze* new_m = so_resize(&tst, 5, 5); so_display(new_m, "\nresize from maze above"); so_play(new_m); Node* node = so_get_node(1,3, new_m); so_show_node(node, "node at (1,3) is "); free(node); new_m = so_destroy(new_m); // free last return 0; }
output for this test
2x4 just created [2,4] Maze ( 0, 0) ( 0, 0) ( 0, 0) ( 0, 0) ( 0, 0) ( 0, 0) ( 0, 0) ( 0, 0) same one, after fill [2,4] Maze ( 0, 0) ( 1, 0) ( 0, 1) ( 1, 1) ( 0, 2) ( 1, 2) ( 0, 3) ( 1, 3) resize from maze above [5,5] Maze ( 0, 0) ( 1, 0) ( 0, 0) ( 0, 0) ( 0, 0) ( 0, 1) ( 1, 1) ( 0, 0) ( 0, 0) ( 0, 0) ( 0, 2) ( 1, 2) ( 0, 0) ( 0, 0) ( 0, 0) ( 0, 3) ( 1, 3) ( 0, 0) ( 0, 0) ( 0, 0) ( 0, 0) ( 0, 0) ( 0, 0) ( 0, 0) ( 0, 0) play(5,5) node at (1,3) is ( 1, 3)
The code for the example
a header file stuff.h
#pragma once #include <memory.h> #include <stdio.h> #include <stdlib.h> typedef struct { int x; int y; } Node; typedef struct { unsigned width; unsigned height; Node* nd; } Maze; Maze* so_create(unsigned W, unsigned H); Maze* so_destroy(Maze* maze); int so_display(Maze* maze, const char* title); Node* so_get_node(unsigned y, unsigned x, Maze* maze); Maze* so_resize( Maze** original, unsigned new_w, unsigned new_h); // shows a single node int so_show_node(Node* node, const char* msg); // helper: just number all maze nodes int so_fill_maze(Maze* maze); int so_play(Maze* maze); // does nothing here
the functions in stuff.c
#include "stuff.h" Maze* so_create(unsigned W, unsigned H) { Maze* one = (Maze*)malloc(sizeof(Maze)); if (one == NULL) return NULL; one->width = W; one->height = H; size_t size = (size_t)W * (size_t)(H) * sizeof(Node); one->nd = (Node*)malloc(size); if (one->nd == NULL) { free(one); return NULL; } memset(one->nd, 0, size); // clear nodes return one; } Maze* so_destroy(Maze* one) { if (one == NULL) return NULL; free(one->nd); free(one); return NULL; } int so_display(Maze* maze, const char* msg) { if (maze == NULL) return -1; // an optional message can be useful if (msg != NULL) printf("%s", msg); printf( " [%u,%u] Maze\n\n", maze->width, maze->height); Node* p = maze->nd; for (unsigned h = 0; h < maze->height; h += 1) { for (unsigned w = 0; w < maze->width; w += 1) so_show_node(p++, NULL); printf("\n"); } return 0; } Node* so_get_node(unsigned x, unsigned y, Maze* maze) { if (maze == NULL) return NULL; if (maze->width < x) return NULL; if (maze->height < y) return NULL; // ok, we have maze and point (x,y) is ok Node* node = malloc(sizeof(Node)); if (node == NULL) return NULL; *node = *(maze->nd + y * maze->width + x); return node; } Maze* so_resize( Maze** original, unsigned new_w, unsigned new_h) { // original is Maze** so we can reset the pointer // here Maze* one = so_create(new_w, new_h); if (one == NULL) return NULL; unsigned lim_w = new_w < (*original)->width ? new_w : (*original)->width; unsigned lim_h = new_h < (*original)->height ? new_h : (*original)->height; Node* p_new = one->nd; Node* p_old = (*original)->nd; // copy nodes to new maze were possible for (unsigned h = 0; h < lim_h; h += 1) for (unsigned w = 0; w < lim_w; w += 1) *(one->nd + h * one->width + w) = *((*original)->nd + h * (*original)->width + w); *original = so_destroy(*original); return one; } int so_show_node(Node* node, const char* msg) { if (node == NULL) return -1; // an optional message can be useful also here if (msg != NULL) printf("%s", msg); printf("(%3d,%3d) ", node->x, node->y); return 0; } int so_fill_maze(Maze* maze) { // each node will have (x,y) = the node position // in maze, just for testing if (maze == NULL) return -1; // no maze Node* p = maze->nd; for (unsigned h = 0; h < maze->height; h += 1) for (unsigned w = 0; w < maze->width; w += 1) p->x = w, p->y = h, p++; return 0; } int so_play(Maze* maze) { if (maze == NULL) return -1; printf("play(%u,%u)\n", maze->width, maze->height); return 0; }
main is just above in the comments.
HIH
#if 0to#if 1it runs clean for me under valgrind. Are you sure that's the code that gives you the "invalid read" error?(size_t)(width * height)better as(size_t)width * heightto less likely overflow.Node *nodes;-->Node *nodes = NULL;asNULLis not certainly a zero pattern.while ((c = getchar()) != 'q') {risks an infinite loop. Better aswhile ((c = getchar()) != 'q' && c != EOF) {.some_type *ptr = NULL or malloc/calloc(...);laterfree(ptr); ptr = NULL or malloc/calloc(...);(repeat as needed), finallyfree(ptr); ptr = NULL;. Note that it is OK to callfree(ptr);even ifptr == NULL.