Suppose I have a function that allocates memory for the caller:
int func(void **mem1, void **mem2) {
*mem1 = malloc(SIZE);
if (!*mem1) return 1;
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;
}
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 return
s:
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.
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;
}