Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is there any other method to handle many 'malloc' failures?

Tags:

c

malloc

I'm trying to write a function in C to solve a math problem. In that function, there are several steps, and each step needs to allocate some memory with the size depending on the calculation results in previous steps (so I can't allocate them all at the beginning of the function). The pseudo code looks like:

int func(){
    int *p1, *p2, *p3, *p4;
    ...

    p1 = malloc(...);
    if(!p1){
        return -1;            //fail in step 1
    }

    ...
    p2 = malloc(...);
    if(!p2){
        free(p1);
        return -2;            //fail in step 2
    }

    ...
    p3 = malloc(...);
    if(!p3){
        free(p1);
        free(p2);
        return -3;            //fail in step 3
    }

    ...
    p4 = malloc(...);
    if(!p4){
        free(p1);
        free(p2);
        free(p3);            /* I have to write too many "free"s here! */
        return -4;           //fail in step 4
    }

    ...
    free(p1);
    free(p2);
    free(p3);
    free(p4);

    return 0;                //normal exit
}

The above way to handle malloc failures is so ugly. Thus, I do it in the following way:

int func(){
    int *p1=NULL, *p2=NULL, *p3=NULL, *p4=NULL;
    int retCode=0;
    ...

    /* other "malloc"s and "if" blocks here */

    ...
    p3 = malloc(...);
    if(!p3){
        retCode = -3;            //fail in step 3
        goto FREE_ALL_EXIT;
    }

    ...
    p4 = malloc(...);
    if(!p4){
        retCode = -4;            //fail in step 4
        goto FREE_ALL_EXIT;
    }

    ...
FREE_ALL_EXIT:
    free(p1);
    free(p2);
    free(p3);
    free(p4);

    return retCode;              //normal exit
}

Although I believe it's more brief, clear, and beautiful now, my team mate is still strongly against the use of 'goto'. And he suggested the following method:

int func(){
    int *p1=NULL, *p2=NULL, *p3=NULL, *p4=NULL;
    int retCode=0;
    ...

    do{

        /* other "malloc"s and "if" blocks here */

        p4 = malloc(...);
        if(!p4){
            retCode = -4;            //fail in step 4
            break;
        }

    ...     
    }while(0);

    free(p1);
    free(p2);
    free(p3);
    free(p4);

    return retCode;              //normal exit
}

Hmmm, it seems a way to avoid the use of 'goto', but this way increases indents, which makes the code ugly.

So my question is, is there any other method to handle many 'malloc' failures in a good code style? Thank you all.

like image 316
Stan Avatar asked Jul 20 '11 01:07

Stan


People also ask

How do you handle a situation when malloc fails?

The same way you'd handle any failure without goto statements: avoid using goto statements! Really, what you need to decide is whether your program can continue after a malloc() failure. If it can't, just exit(), hopefully after providing information on why your program is exiting.

Do we need to free if malloc fails?

No, never. If malloc returns NULL, that indicates an error, and you should probably abort.

How do I protect my malloc?

The way to avoid that is to avoid malloc in the first place. Use local ( auto or register ) variables whenever you may. If you are allocating an object of a primitive data type ( int , double , void* …) with malloc (and conceptually this is not an array of length 1 ) you are most probably on the wrong track.

What causes malloc to fail?

malloc() normally fails today for one of two reason: (1) attempt to allocate more memory then is available, or (2) memory has been previously trashed (most common reason) such as buffer overflows and using uninitialized pointers (although there are a whole host of other causes).


2 Answers

goto in this case is legitimate. I see no particular advantage to the do{}while(0) block as its less obvious what pattern it is following.

like image 138
Yann Ramin Avatar answered Sep 23 '22 10:09

Yann Ramin


First of all, there's nothing wrong with goto—this is a perfectly legitimate use of goto. The do { ... } while(0) with break statements are just gotos in disguise, and it only serves to obfuscate the code. Gotos are really the best solution in this case.

Another option is to put a wrapper around malloc (e.g. call it xmalloc) which kills the program if malloc fails. For example:

void *xmalloc(size_t size)
{
    void *mem = malloc(size);
    if(mem == NULL)
    {
        fprintf(stderr, "Out of memory trying to malloc %zu bytes!\n", size);
        abort();
    }
    return mem;
}

Then use xmalloc everywhere in place of malloc, and you no longer need to check the return value, since it will return a valid pointer if it returns at all. But of course, this is only usable if you want allocation failures to be an unrecoverable failure. If you want to be able to recover, then you really do need to check the result of every allocation (though honestly, you'll probably have another failure very soon after).

like image 22
Adam Rosenfield Avatar answered Sep 23 '22 10:09

Adam Rosenfield