So assuming I have two structs where game_t struct has a board_t type pointer to remember all the information about the board. The board member variable cells has dynamic memory via
board->cells = calloc(20 * 10, sizeof(int));
And I attempt to free this memory via
free(game->board->cells);
What is going wrong? (I am assuming I am accessing cells incorrectly somehow)
#ifndef COMMON_H
#define COMMON_H
#include <stddef.h>
// Bitflags enable us to store cell data in integers!
#define FLAG_PLAIN_CELL 0x0
#define FLAG_SNAKE 0x1
#define FLAG_WALL 0x2
#define FLAG_FOOD 0x4
/** Enumerated key input options:
* INPUT_UP, INPUT_DOWN, INPUT_LEFT, INPUT_RIGHT, and INPUT_NONE
*/
typedefenum input_key {
INPUT_UP,
INPUT_DOWN,
INPUT_LEFT,
INPUT_RIGHT,
INPUT_NONE
} input_key_t;
/** Struct containing board data.
*
* Fields:
* - width: the width of the board.
* - height: the height of the board.
* - cells: a pointer to the first integer in an array of integers representing
* each board cell.
*/
typedefstruct board {
size_t width;
size_t height;
int* cells; // array of integers containing data re each cell via bitmap
snake_t* snake;
} board_t;
/** Struct containing game data.
*
* You may need to modify this in part 2 of the project!
*
* Fields:
* - game_over: if the game is over, then 1; else, 0.
* - score: current game score. Starts at 0.
* - board: a pointer to the game's board struct
*/
typedefstruct game {
int game_over; // 1 if game is over, 0 otherwise
int score; // game score: 1 point for every food eaten
board_t* board; // pointer to the board struct for this game
} game_t;
#endif
#include "game.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include "linked_list.h"
#include "mbstrings.h"
/** Sets a random space on the given board to food.
* Arguments:
* - `board`: a pointer to the board to be updated.
*/
void place_food(board_t* board) {
int food_index = generate_index(board->width * board->height);
if (*(board->cells + food_index) == FLAG_PLAIN_CELL) {
*(board->cells + food_index) = FLAG_FOOD;
} else {
place_food(board);
}
}
* Cleans up on game over — should free any allocated memory so that the
* LeakSanitizer doesn't complain.
* Arguments:
* - `game`: a pointer the current game struct.
*/
void teardown(game_t* game) {
free(game->board->cells);
}
#define _XOPEN_SOURCE_EXTENDED 1
#include <curses.h>
#include <errno.h>
#include <locale.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include "game.h"
#include "game_over.h"
#include "game_setup.h"
#include "mbstrings.h"
#include "render.h"
/** Gets the next input from the user, or returns INPUT_NONE if no input is
* provided quickly enough.
*/
input_key_t get_input() {
/* DO NOT MODIFY THIS FUNCTION */
int input = getch();
if (input == KEY_UP) {
return INPUT_UP;
} elseif (input == KEY_DOWN) {
return INPUT_DOWN;
} elseif (input == KEY_LEFT) {
return INPUT_LEFT;
} elseif (input == KEY_RIGHT) {
return INPUT_RIGHT;
} else {
// if the input isn't an arrow key, we treat it as no input (could add
// other keys in if we want other commands, like 'pause' or 'quit')
return INPUT_NONE;
}
/* DO NOT MODIFY THIS FUNCTION */
}
/** Helper function that procs the GAME OVER screen and final key prompt.
*/
void end_game(game_t* game) {
// Game over!
// Free any memory we've taken
teardown(game);
//free(game->board->cells);
// tell ncurses that we're done
endwin();
}
int main(int argc, char** argv) {
// Main program function — this is what gets called when you run the
// generated executable file from the command line!
// initialize board from command line arguments
game_t game;
board_t board;
snake_t snake;
game.board = &board;
board.snake = &snake;
int snake_grows;
switch (argc) {
case (2):
snake_grows = atoi(argv[1]);
if (snake_grows != 1 && snake_grows != 0) {
printf(
"snake_grows must be either 1 (grows) or 0 (does not ""grow)\n");
return 0;
}
initialize_game(&game, NULL);
break;
case (3):
snake_grows = atoi(argv[1]);
if (snake_grows != 1 && snake_grows != 0) {
printf(
"snake_grows must be either 1 (grows) or 0 (does not ""grow)\n");
return 0;
} elseif (*argv[2] == '\0') {
initialize_game(&game, NULL);
break;
}
initialize_game(&game, argv[2]);
break;
case (1):
default:
printf("usage: snake <GROWS: 0|1> [BOARD STRING]\n");
return 0;
}
/*
while (1)
{
usleep(100);
update(game, none, snake_grows);
render_game(game_t * game)
}
end_game(&game);
*/
}
Error
1 2 3 4 5 6 7 8 9 10
==497==ERROR: LeakSanitizer: detected memory leaks
Direct leak of 800 byte(s) in 1 object(s) allocated from:
#0 0x7f3f5430edc6 in calloc (/lib/x86_64-linux-gnu/libasan.so.5+0x10ddc6)
#1 0x55e228513927 in initialize_default_board src/game_setup.c:27
#2 0x55e228513c7f in initialize_game src/game_setup.c:56
#3 0x55e228514b4e in main src/snake.c:81
#4 0x7f3f53fcc0b2 in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x270b2)
SUMMARY: AddressSanitizer: 800 byte(s) leaked in 1 allocation(s).
You could have found this issue on your own by using the debugger.
You could have checked your assumption that it was actually called when I suggested it in my prior post.
If none of those things led you to a solution, you should have removed stuff from your program until you couldn't remove any more without solving your problem.
If this still didn't lead you to a solution (and it was very likely do so), you should have posted this small, complete, compilable reproduction of your problem in the forum.
Thanks for the help not sure how such a simple issue got passed me.
They haven't taught us to use debuggers yet and I hesitated to include the full file because there were a lot of files.
For a minimal reproducible example, there are a lot of files and I didn't even really understand the issue so I was unable to really come up with one.
Apologies for the issues.
should be the second week or so but is often not even included at all!
a modern debugger, like visual studio, is fairly simple to use.
the most basic approach is:
1) compile it in debug mode. this lets you see the source code, line by line and variable by variable as you execute the program.
2) run it in debug mode, from your IDE.
3) put a 'break point' where you think there is an issue.
4) when the program stops running at your break point, look at the values of the variables for problems. you can use a 'watch'ed variable to quickly step code until the value changes. You can set conditions on the break point, so it stops when you get a weird value in a specific variable (for an example) so you can try to see where that came from.
your mental process is something like:
The program does something stupid here. Why? What values in these 15 variables is causing that to happen? ... stop the code when it happens, look at the values ... O, I see, x = b/c and c is zero this time around! ... /fix
A debugger is not a panacea, just a tool to help you check your assumptions. Simply knowing how to use one isn't enough. You also need to know when to use one and what to look for. The best way that I know of to learn this is by watching a professional do it.
For a minimal reproducible example, there are a lot of files and I didn't even really understand the issue so I was unable to really come up with one.
First you take a backup of your source code. Then you just rip functionality out of your program a little bit at a time, testing after each removal, until proceeding further would quiet Valgrind. The last program you end up with is the minimal example.
This approach is useful basically because you don't need to understand the problem to use it. Instead you'll narrow in on it iteratively.