What is the best way to free memory after returnin

2019-01-22 09:31发布

Suppose I have a function that allocates memory for the caller:

int func(void **mem1, void **mem2) {
    *mem1 = malloc(SIZE);
    if (!*mem1) return 1;

    *mem2 = malloc(SIZE);
    if (!*mem2) {
        /* ... */
        return 1;
    }

    return 0;
}

I'd like to hear your feedback on the best way to free() the allocated memory in case the second malloc() fails. You can imagine a more elaborate situation with more error exit points and more allocated memory.

9条回答
我只想做你的唯一
2楼-- · 2019-01-22 10:11

This is a readable alternative:

int func(void **mem1, void **mem2) {
  *mem1 = malloc(SIZE);
  *mem2 = malloc(SIZE);
  if (!*mem1 || !*mem2) {
    free(*mem2);
    free(*mem1);
    return 1;
  }
  return 0;
}
查看更多
我只想做你的唯一
3楼-- · 2019-01-22 10:14

Does the caller do anything useful with the memory blocks which have been correctly allocated before the failure? If not, the callee should handle the deallocation.

One possibility to do the cleanup efficiently is using do..while(0), which allows to break where your example returns:

int func(void **mem1, void **mem2)
{
    *mem1 = NULL;
    *mem2 = NULL;

    do
    {
        *mem1 = malloc(SIZE);
        if(!*mem1) break;

        *mem2 = malloc(SIZE);
        if(!*mem2) break;

        return 0;
    } while(0);

    // free is NULL-safe
    free(*mem1);
    free(*mem2);

    return 1;
}

If you do a lot of allocations, you might want to use your freeAll() function to do the cleanup here as well.

查看更多
家丑人穷心不美
4楼-- · 2019-01-22 10:17

I'm a little horrified by all the recommendations for a goto statement!

I have found that the use of goto leads to confusing code which is more likely to give rise to programmer errors. My preference now is to avoid its use altogether, except in the most extreme situations. I'd almost never use it. Not because of academic perfectionism, but because a year or more later it always seems more difficult to recall the overall logic than with the alternative I will suggest.

Being one who loves to refactor things to minimize my option to forget stuff (like clearing a pointer), I'd add a few functions first. I'll presume it's likely that I would be reusing these quite a bit in the same program. Function imalloc() would do the malloc operation with the indirect pointer; ifree() would undo this. cifree() would free memory conditionally.

With that in hand, my version of the code (with a third arg, for sake of demonstration) would be like this:

// indirect pointer malloc
int imalloc(void **mem, size_t size)
{
   return (*mem = malloc(size));
}

// indirect pointer free
void ifree(void **mem)
{
   if(*mem)
   {
     free(*mem);
     *mem = NULL;
   }
}

// conditional indirect pointer free
void cifree(int cond, void **mem)
{
  if(!cond)
  {
    ifree(mem);
  }
}

int func(void **mem1, void **mem2, void **mem3)
{
   int result = FALSE;
   *mem1 = NULL;
   *mem2 = NULL;
   *mem3 = NULL;

   if(imalloc(mem1, SIZE))
   {
     if(imalloc(mem2, SIZE))
     {
       if(imalloc(mem3, SIZE))
       {
         result = TRUE;
       }            
       cifree(result, mem2);
     }
     cifree(result, mem1);
   }
  return result;
}

I prefer to have only one return from a function, at the end. Jumping out in between is quick (and in my opinion, kind of dirty). But more significantly, allows you to easily bypass associated cleanup code unintentionally.

查看更多
登录 后发表回答