Reputation: 13
I am trying to create a 2d array of struct grid_t, and am getting memory leak warnings via address sanitiser, and eventually a seg fault in certain conditions.
There may be various points in my code causing this, but I thought knowing what was going wrong here would point me in the right direction to fixing the rest.
I am new to C and thus to memory management, so all feedback is welcome and appreciated!
void createGridArray(atom_t* ATOM) {
ATOM -> grid = (grid_t**) malloc(WIDTH * sizeof(grid_t*));
grid_t *nullGrid = malloc(sizeof(grid_t));
grid_t temp = {NULL, 0};
*nullGrid = temp;
for (int i = 0; i < WIDTH; i++) {
(ATOM -> grid)[i] = malloc(HEIGHT * sizeof(grid_t));
for (int j = 0; j < HEIGHT; j++) {
(ATOM -> grid)[i][j] = *nullGrid;
}
}
//free(nullGrid); <- do I do this now?
return;
}
Upvotes: 1
Views: 1763
Reputation: 49
The C memory allocation is easier when you think it as a "loan".
Somehow, the system loan you some memory when you call malloc(), and you have to give it back with free().
Upvotes: 0
Reputation: 36637
Firstly, don't cast the return from malloc()
. It is not required in C, and can obscure serious errors.
Second, don't hard-code the type into the malloc()
call. For example,
ATOM->grid = (grid_t**) malloc(WIDTH * sizeof(grid_t*));
would be replaced by
ATOM->grid = malloc(WIDTH * sizeof(*(ATOM->grid)));
This ensures the memory allocated is of the required size, regardless of what ATOM->grid
is a pointer to.
To answer your question, to release all memory, you need to pass every nonNULL
pointer returned by malloc()
to free()
. Exactly once.
So, if you allocate like this
ATOM->grid = malloc(WIDTH * sizeof(*(ATOM->grid)));
grid_t *nullGrid = malloc(sizeof(*nullGrid));
grid_t temp = {NULL, 0};
*nullGrid = temp;
for (int i = 0; i < WIDTH; i++)
{
(ATOM -> grid)[i] = malloc(HEIGHT * sizeof(*((ATOM->grid)[i])));
for (int j = 0; j < HEIGHT; j++) {
(ATOM -> grid)[i][j] = *nullGrid;
}
the one way of deallocating would be
for (int i = 0; i < WIDTH; i++)
{
free((ATOM -> grid)[i]);
}
free(ATOM->grid);
free(nullGrid);
In this case, you cannot safely free(ATOM->grid)
before any free((ATOM -> grid)[i])
(unless you store all of the (ATOM->grid)[i]
somewhere else, which sort of defeats the point). The individual (ATOM->grid)[i]
may be freed in any order (as long as each is released exactly once).
Lastly, check the pointers returned by malloc()
. It returns NULL
when it fails, and dereferencing a NULL
pointer gives undefined behaviour.
Upvotes: 3
Reputation: 50912
Yes, you need to free(nullGrid);
here to avoid a memory leak.
But actually you can simplify your code to this:
void createGridArray(atom_t* ATOM) {
ATOM -> grid = (grid_t**) malloc(WIDTH * sizeof(grid_t*));
grid_t nullGrid = {NULL, 0};
for (int i = 0; i < WIDTH; i++) {
(ATOM -> grid)[i] = malloc(HEIGHT * sizeof(grid_t));
for (int j = 0; j < HEIGHT; j++) {
(ATOM -> grid)[i][j] = nullGrid;
}
}
}
There is need for mallocing nullGrid here at all. BTW the return
at the end of a void function is implicit.
You wouldn't do this either:
void Foo()
{
...
int *temp = malloc(sizeof(int));
*temp = ...;
...
bar = *temp;
...
free(temp);
}
but rather:
void Foo()
{
...
int temp
temp = ...;
...
bar = temp;
...
}
Upvotes: 0