简体   繁体   中英

Memory leaking in C, malloc inside function

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;
}

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 non NULL 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.

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;

   ...
}

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().

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM