views:

375

answers:

9

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.

A: 

My own inclination is to create a variable argument function that frees all non-NULL pointers. Then the caller can handle the error case:

void *mem1 = NULL;
void *mem2 = NULL;

if (func(&mem1, &mem2)) {
    freeAll(2, mem1, mem2);
    return 1;
}
Andrew Keeton
Actually, there is no penalty in calling free on a NULL pointer. You don't have to check for NULL. But it is important to initialize to NULL.
ypnos
@ypnos: You're thinking of C++ delete. It is invalid to pass NULL to free().
Captain Segfault
Andrew Keeton
If you're talking about using varargs, you will run into two problems with this. 1. What is the last parameter before the variable argument list? 2. How do you know where the end of the variable argument list is? (Stopping on NULL like execl() does won't let you pass multiple NULL pointers.)
bk1e
@bk1e This was actually my first time using a varargs function so when I got around to writing it (after my post) I had to include the number of elements to be freed.
Andrew Keeton
+1  A: 

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.

Christoph
No, the caller is just going to free if needed and return. Isn't this code a thinly-veiled goto? Not that I'm opposed to using goto's for this purpose...
Andrew Keeton
@FunkyMo: Yes, it's basically a `goto`, but with the restriction that you can only skip the rest of the block, ie it's impossible to do evil things[tm]; I personally find this cleaner than using a `goto`, which I only use to break out of newsted loops....
Christoph
@FunkyMo: I use this pattern quite often: it allows for some nifty macro magic: http://stackoverflow.com/questions/569573/what-is-a-good-programming-pattern-for-handling-return-values-from-stdio-file-wri/569659#569659
Christoph
+18  A: 

I know people are loathe to use them, but this is the perfect situation for a goto in C.

int func( void** mem1, void** mem2 )
{
    int retval = 0;
    *mem1 = malloc(SIZE);
    if (!*mem1)
        goto err;

    *mem2 = malloc(SIZE);
    if (!*mem2) 
        goto err;
// ...     
    goto out;
// ...
err:
    retval = 1;
    if( *mem1 ) free( *mem1 );
    if( *mem2 ) free( *mem2 );
out:
    return retval;
}
FreeMemory
goto statements are fine in certain situations. We see this type of cleanup in the linux kernel a lot. It works well and is easy to understand.
Steve Lazaridis
@Steve Lazaridis: Exactly. Working with the Linux kernel taught me that sometimes, goto is ok. :)
FreeMemory
That won't work reliably unless you pre-zero the *mem1 and *mem2.
Jonathan Leffler
@Jonathtan Leffler: I'm assuming the caller is a "good C citizen." Either that, or, yeah, you'd have to zero them out.
FreeMemory
If you pre-zero the *mems you don't need to test if(*mem).
Dour High Arch
+5  A: 

This is where a goto is appropriate, in my opinion. I used to follow the anti-goto dogma, but I changed that when it was pointed out to me that do { ... } while (0); compiles to the same code, but isn't as easy to read. Just follow the some basic rules, like not going backwards with them, keeping them to a minimum, only using them for error conditions, etc...

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

    *mem1 = malloc(SIZE);
    if(!*mem1)
        goto err;

    *mem2 = malloc(SIZE);
    if(!*mem2)
        goto err;

    return 0;
err:
    if(*mem1)
        free(*mem1);
    if(*mem2)
        free(*mem2);

    *mem1 = *mem2 = NULL;

    return 1;
}
Ryan Graham
+4  A: 

This is a bit controversial, but I think the goto approach used in Linux kernel actually works pretty well in this situation:

int get_item(item_t* item)
{
  void *mem1, *mem2;
  int ret=-ENOMEM;
  /* allocate memory */
  mem1=malloc(...);
  if(mem1==NULL) goto mem1_failed;

  mem2=malloc(...);
  if(mem2==NULL) goto mem2_failed;

  /* take a lock */
  if(!mutex_lock_interruptible(...)) { /* failed */
    ret=-EINTR;
    goto lock_failed;
  }

  /* now, do the useful work */
  do_stuff_to_acquire_item(item);
  ret=0;

  /* cleanup */
  mutex_unlock(...);

lock_failed:
  free(mem2);

mem2_failed:
  free(mem1);

mem1_failed:
  return ret;
}
jpalecek
A: 

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.

digijock
This function leaks memory if it returns FALSE and frees memory when it returns TRUE, not what the questioner asked.
Dour High Arch
Corrected the typo - the cond comparision should have been (!cond).
digijock
Dour High Arch - It would be nice if you would retract the comment since it is incorrect. The basic scheme I presented does in fact work quite well, does not leak, and does not return invalid pointers.
digijock
I would also add that this addresses directly and precisely what the original questioner asked.
digijock
A: 

If the above goto statements horrify you for some reason, you can always do something like this:

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

    *mem2 = malloc(SIZE);
    if (!*mem2) {
        /* Insert free statement here */
        free(*mem1);
        return 1;
    }

    return 0;
}

I use this method pretty regularly, but only when it's very clear what's going on.

+2  A: 

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;
}
Paul Hankin
+1  A: 

Personally; I have a resource tracking library (basically a balanced binary tree) and I have wrappers for all allocation functions.

Resources (such as memory, sockets, file descriptors, semaphores, etc - anything you allocate and deallocate) can belong to a set.

I also have an error handling library, whereby the first argument to each function is an error set and if something goes wrong, the function experiencing an error submits an error into the error set.

If an error set contains an error, no functions execute. (I have a macro at the top of every function which causes it to return).

So multiple mallocs look like this;

mem[0] = malloc_wrapper( error_set, resource_set, 100 );
mem[1] = malloc_wrapper( error_set, resource_set, 50 );
mem[2] = malloc_wrapper( error_set, resource_set, 20 );

There is no need to check the return value, because if an error occurs, no following functions will execute, e.g. the following mallocs never occur.

So, when the time comes for me to deallocate resources (say at the end of a function, where all the resources used internally by that function have been placed into a set), I deallocate the set. It's just one function call.

res_delete_set( resource_set );

I don't need to specifically check for errors - there are no if()s in my code checking return values, which makes it maintainable; I find the profliferation of in-line error check destroys readability and so maintainability. I just have a nice plain list of function calls.

It's art, man :-)

Blank Xavier