Page 1 of 1

Problem calling free() from another function [SOLVED]

Posted: Tue Oct 21, 2014 7:40 pm
by jmcintyretech
I'm getting a segmentation fault when I try to free memory I allocate for a 2d array. The following causes a segmentation fault:
/* This function is the main game loop

 */
void game_loop()
{
	/* Store the user input for logic */
	Input input = NONE;

	/* Create game variables */
	Grid** grid;
	int score;
	int goal;

	/* Initialize a new game */
	new_game(grid, &score, &goal);

	while(true)
	{
		/* Get user input */
		input = get_input();
			
		if (input == QUIT)
		{
			end_game(grid);
			break;
		}
	}
	
	/* Go to the score loop */
	score_loop();
	
}

/* This function allocates a grid for a new game and initializes game data

Param: grid
Param: score
Param: goal

*/
void new_game(Grid** grid , int* score, int* goal)
{
	grid = malloc(GRID_SIZE * GRID_SIZE * sizeof(Grid));

	int i;
	for (i = 0; i < GRID_SIZE; i++)
	{
		grid = malloc(GRID_SIZE * sizeof(Grid));
	}

       /* Test load some data into this 2d array */
	int r;
	int c;
	for(r = 0; r < GRID_SIZE; r++)
	{
		for (c = 0; c < GRID_SIZE; c++)
		{
			grid[r][c] = ONE;
			printf("%d ", grid[r][c]);
		}

		printf("\n");
	}

	*score = 0;
	*goal = 15;


}

/* This function frees memory from a game

Param: grid

*/
void end_game(Grid** grid)
{
	int i;
	for (i = 0; i < GRID_SIZE; i++)
	{
		free(grid);
	}

	free(grid);
}

So the main game loop calls new_game(grid, &score, &goal), loops until the user quits (I haven't coded any of the actual game stuff yet) and then calls end_game(grid) when the user quits.

Now, when testing, this does NOT cause a segmentation fault:
/* This function allocates a grid for a new game and initializes game data

Param: grid
Param: score
Param: goal

*/
void new_game(Grid** grid , int* score, int* goal)
{
	grid = malloc(GRID_SIZE * GRID_SIZE * sizeof(Grid));

	int i;
	for (i = 0; i < GRID_SIZE; i++)
	{
		grid = malloc(GRID_SIZE * sizeof(Grid));
	}

       /* Test load some data into this 2d array */
	int r;
	int c;
	for(r = 0; r < GRID_SIZE; r++)
	{
		for (c = 0; c < GRID_SIZE; c++)
		{
			grid[r][c] = ONE;
			printf("%d ", grid[r][c]);
		}

		printf("\n");
	}

	*score = 0;
	*goal = 15;

       /* Test free the 2d array allocated in this function */
	for (i = 0; i < GRID_SIZE; i++)
	{
		free(grid);
	}

	free(grid);

}

My question: why do I get an error when I pass the Grid** 2d array to an external function and free, but do not get an error when I deallocate in the same location that I allocated the memory from? Am I not passing the 2d array "pointer pointer" properly?

If any of the above does make sense, please ask and I will clarify.

Re: Problem calling free() from another function

Posted: Wed Oct 22, 2014 6:52 am
by bbguimaraes
grid should be just GRID_SIZE * sizeof(Grid) in size. You misunderstood how a 2d array is implemented using pointers to pointers. The "first level" pointer (grid) points to an array of pointers of size equal to the number of rows. Each pointer in this array (grid) points to another array of size equal to the number of columns. A diagram:
// If you want the grid to be:
// a b c d
// e f g h
// i j k l

// You will have the following structure:

// One array is allocated just to store the addresses of each row (a pointer to
// pointers).
char ** grid = malloc(3 * sizeof(char));
// At each position of this array, another array is allocated to store the
// actual data of each row.
grid[0] = malloc(4 * sizeof(char));
grid[0][0] = 'a'; grid[0][1] = 'b'; grid[0][2] = 'c'; grid[0][3] = 'd';
grid[1] = malloc(4 * sizeof(char));
grid[1][0] = 'e'; grid[1][1] = 'f'; grid[1][2] = 'g'; grid[1][3] = 'h';
grid[2] = malloc(4 * sizeof(char));
grid[2][0] = 'i'; grid[1][2] = 'j'; grid[2][2] = 'k'; grid[2][3] = 'l';
When freeing, the process is analogous:
free(grid[0]);
free(grid[1]);
free(grid[2]);
free(grid);
Note this is not the most efficient way of doing this.

Re: Problem calling free() from another function

Posted: Wed Oct 22, 2014 4:37 pm
by jmcintyretech
So I'm still having trouble passing this Grid** in and out of functions. I asked one of my profs and she noticed this:

Code: Select all

grid = malloc(GRID_SIZE * sizeof(Grid));
should be

Code: Select all

grid = malloc(GRID_SIZE * sizeof(Grid*));
Which I originally missed. I do understand that I am creating a pointer to an array of pointers, basically. So her explanation made sense. I'll keep you guys posted on anything further I run into.

Re: Problem calling free() from another function

Posted: Thu Oct 23, 2014 5:41 am
by bbguimaraes
jmcintyretech wrote:should be

Code: Select all

grid = malloc(GRID_SIZE * sizeof(Grid*));
My bad :oops:
jmcintyretech wrote:So I'm still having trouble passing this Grid** in and out of functions.
Remember a pointer-to-pointer is still passed by value:
#include <stdio.h>
#include <stdlib.h>


void f0(char ** pp) {
    pp = malloc(3);
}

void f1(char *** pp) {
    *pp = malloc(3);
}

int main() {
    char ** pp = NULL;
    f0(pp);
    printf("%p\n", pp); // prints NULL
    f1(&pp);
    printf("%p\n", pp); // prints some address
}
If you're in c++, you could use a reference (less explicit, IMHO):
void f2(char **& pp) { // called with f2(pp)
    pp = malloc(3);
}

Re: Problem calling free() from another function

Posted: Thu Oct 23, 2014 10:25 am
by jmcintyretech
Got it! I asked a professor at school and she caught this as well.

Here are the working functions:
/* This function is the main entry point for the program */
int main()
{
	Grid** grid;

	new_game(&grid);	
	end_game(&grid);

	return 0;
}

/* This function allocates a 2d array */
void new_game(Grid***  grid)
{
	*grid = (Grid**) malloc(GRID_SIZE * sizeof(Grid*));

	int i;
	for (i = 0; i < GRID_SIZE; i++)
	{
		(*grid) = (Grid*) malloc(GRID_SIZE * sizeof(Grid));
	}

}

/* This function deallocates a 2d array */
void end_game(Grid*** grid)
{
	int i;
	for (i = 0; i < GRID_SIZE; i++)
	{
		free((*grid));
	}

	free(*grid);
}

Re: Problem calling free() from another function

Posted: Thu Oct 23, 2014 10:36 am
by bbguimaraes
\p/