What is the best way to free memory after returning from an error?

后端 未结 9 2241
一整个雨季
一整个雨季 2020-12-13 22:00

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

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

            


        
相关标签:
9条回答
  • 2020-12-13 22:29

    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;
    }
    
    0 讨论(0)
  • 2020-12-13 22:29

    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.

    0 讨论(0)
  • 2020-12-13 22:31

    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;
    }
    
    0 讨论(0)
提交回复
热议问题