40

Suppose this is a part of my code:

 int foo()
 {  
    char *p, *q ;
    if((p = malloc(BUFSIZ)) == NULL) {
        return ERROR_CODE;
    }
    if((q = malloc(BUFSIZ)) == NULL) {
        free(p)
        return ERROR_CODE;
    }
    /* Do some other work... */

    free(p);
    free(q);  
 }

Since it's possible that the first malloc is successful but the second one fails, I use free(p) in the second "error handler". But what if there are more malloc's and what if I want to modify the code (adjusting their orders, adding or deleting some malloc)?

I know in C++ there are things like RAII and exception safe, etc. But in general, what is the correct way to handle malloc failure in C? (maybe using some goto?)

Roun
  • 1,449
  • 1
  • 18
  • 25
  • 7
    Pedantic note: In general, a return value from `malloc()` of `NULL` does not always mean an OOM failure occurred. If the size of the memory requested is 0, `NULL` is an OK response. So if the size could be 0, `if (p == NULL & size != 0)` is a better OOM test. – chux - Reinstate Monica Dec 12 '14 at 20:11
  • 1
    If you're writing platform-specific code, note that the C libraries for *some* OSen now actually specify that `malloc` doesn't fail, at least on a valid argument (e.g. you're not supposed to ever need to check the result of `malloc` on iOS). – Alex Celeste Dec 12 '14 at 21:20
  • In one of our C projects we simply wrote a couple macros to help emulate a special-purpose form of RAII. It works really well (one point of resource acquisition, one point of resource destruction, no code duplication, and it works with stuff other than memory as well) as long as we use the macros correctly. – Thomas Dec 13 '14 at 03:38
  • @Thomas: Do you mind sharing it here? – Roun Dec 13 '14 at 04:16
  • If that was part of your code I'd be worried because it won't compile due to missing parentheses after `NULL` ;) – oarfish Dec 13 '14 at 14:10

6 Answers6

43

Your code is fine, but for lots of variables, I'd prefer:

int
foo()
{
    char *p = NULL;
    char *q = NULL;
    int ret = 0;

    if (NULL == (p = malloc(BUFSIZ)))
    {
        ret = ERROR_CODE;
        goto error;
    }

    // possibly do something here

    if (NULL == (q = malloc(BUFSIZ)))
    {
        ret = ERROR_CODE;
        goto error;
    }

    // insert similar repetitions

    // hopefully do something here

  error:
    free (p);
    free (q);
    return ret;
}

Note that freeing NULL is defined as a no-op.

This avoids n levels of indent for n variables. You can clean up filehandles etc. similarly (though you'll have to put a condition around the close()).

Now, if you know you can allocate them all at once, then dasblinkenlight has a good answer, but here's another way:

int
foo()
{
    int ret = 0;
    char *p = malloc(BUFSIZ);
    char *q = malloc(BUFSIZ);
    char *r = malloc(BUFSIZ);
    if (!p || !q || !r)
    {
        ret = ERROR_CODE;
        goto exit;
    }

    // do something

  exit:
    free(p);
    free(q);
    free(r);
    return ret;
}

Final possibility: if you actually want to exit the program on malloc fail, consider using mallopt's M_CHECK_ACTION option. This makes malloc() faults get checked, and calls abort(), possibly printing a helpful message.

From the man page:

NAME

mallopt - set memory allocation parameters

SYNOPSIS

  #include <malloc.h>

  int mallopt(int param, int value);

DESCRIPTION

The mallopt() function adjusts parameters that control the behavior of the memory-allocation functions (see malloc(3)). The param argument specifies the parameter to be modified, and value specifies the new value for that parameter.

The following values can be specified for param:

M_CHECK_ACTION

Setting this parameter controls how glibc responds when various kinds of programming errors are detected (e.g., freeing the same pointer twice). The 3 least significant bits (2, 1, and 0) of the value assigned to this parameter determine the glibc behavior, as follows:

Bit 0: If this bit is set, then print a one-line message on stderr that provides details about the error. The message starts with the string "*** glibc detected ***", followed by the program name, the name of the memory-allocation function in which the error was detected, a brief description of the error, and the memory address where the error was detected.

Bit 1: If this bit is set, then, after printing any error message specified by bit 0, the program is terminated by calling abort(3). In glibc versions since 2.4, if bit 0 is also set, then, between printing the error message and aborting, the program also prints a stack trace in the manner of backtrace(3), and prints the process's memory mapping in the style of /proc/[pid]/maps (see proc(5)).

Bit 2: (since glibc 2.4) This bit has an effect only if bit 0 is also set. If this bit is set, then the one-line message describing the error is simplified to contain just the name of the function where the error was detected and the brief description of the error.

abligh
  • 24,573
  • 4
  • 47
  • 84
  • 2
    And if you really hate GOTO or it is not allowed in your company, you can easily replace it with a do{}while(false) and break – Falco Jan 08 '15 at 12:13
27

Since it is perfectly OK to pass NULL to free(), you could allocate everything that you need in a "straight line", check everything in a single shot, and then free everything once you are done, regardless of whether or not you have actually done any work:

char *p = malloc(BUFSIZ);
char *q = malloc(BUFSIZ);
char *r = malloc(BUFSIZ);
if (p && q && r) {
    /* Do some other work... */
}
free(p);
free(q);
free(r);

This works as long as there are no intermediate dependencies, i.e. you do not have structures with multi-level dependencies. When you do, it is a good idea to define a function for freeing such a structure, without assuming that all memory blocks are non-NULL.

Sergey Kalinichenko
  • 714,442
  • 84
  • 1,110
  • 1,523
  • 1
    This is neater than my solution, but only works if you know the `malloc` size of `q` and `r` immediately. If there is an intervening function called (which needs `p` to be allocated), it won't work well, as you'll need `n` levels of indent. +1 anyway. – abligh Dec 12 '14 at 20:06
  • 1
    I am actually in the case mentioned by @abligh , where computing the size of `q` might need using `p`. The example in my question is a simplified version for illustration. Thanks for the neat answer anyway. :) – Roun Dec 12 '14 at 20:16
  • 2
    What is really nice about this approach is that `malloc()` and `free()` are at the _same_ level of code and only 1 `free()` exists for each 1 `malloc()`. Certainly not always a doable approach, but when possible, it is clean and maintainable. – chux - Reinstate Monica Dec 12 '14 at 21:10
5

For large numbers of allocations, I would invest the time in creating a memory manager that keeps track of the allocations. That way, you never have to worry about leaks, regardless of whether or not the function succeeds.

The general idea is to create a wrapper for malloc that records successful allocations, and then frees them on request. To free memory, you simply pass a special size to the wrapper function. Using a size of 0 to free memory is appropriate if you know that none of your actual allocations will be for 0 sized blocks. Otherwise, you may want to use ~0ULL as the request-to-free size.

Here's a simple example that allows up to 100 allocations between frees.

#define FREE_ALL_MEM 0

void *getmem( size_t size )
{
    static void *blocks[100];
    static int count = 0;

    // special size is a request to free all memory blocks
    if ( size == FREE_ALL_MEM )
    {
        for ( int i = 0; i < count; i++ )
            free( blocks[i] );
        count = 0;
        return NULL;
    }

    // using a linked list of blocks would allow an unlimited number of blocks
    // or we could use an array that can be expanded with 'realloc'
    // but for this example, we have a fixed size array
    if ( count == 100 )
        return NULL;

    // allocate some memory, and save the pointer in the array
    void *result = malloc( size );
    if ( result )
        blocks[count++] = result;

    return result;
}

int foo( void )
{
    char *p, *q;

    if ( (p = getmem(BUFSIZ)) == NULL ) {
        return ERROR_CODE;
    }
    if ( (q = getmem(BUFSIZ)) == NULL ) {
        getmem( FREE_ALL_MEM );
        return ERROR_CODE;
    }

    /* Do some other work... */

    getmem( FREE_ALL_MEM );
    return SUCCESS_CODE;
}
user3386109
  • 34,287
  • 7
  • 49
  • 68
3

I think the first answer is the most general purpose as it can be used for errors other than those caused by malloc. However I would remove the gotos and use a single pass while loop like so.

int foo()
{
  char *p = NULL;
  char *q = NULL;
  int ret = 0;
  do {
    if (NULL == (p = malloc(BUFSIZ)))
    {
      ret = ERROR_CODE;
      break;
    }

    // possibly do something here

    if (NULL == (q = malloc(BUFSIZ)))
    {
      ret = ERROR_CODE;
      break;
    }

    // insert similar repetitions

    // hopefully do something here
  } while(0);
  free (p);
  free (q);
  return ret;
}
Gray
  • 47
  • 1
  • 2
    Concerning blind elimination of `goto`s and using always-false `do` loops instead, [this answer](http://stackoverflow.com/a/2809622/673852) is worth reading. – Ruslan Dec 13 '14 at 10:42
  • You haven't removed the `goto` at all, just hidden it behind a macro that obfuscates the control flow. Well done, this is infinitely worse. – Alex Celeste Jan 13 '15 at 21:43
  • Hate endless loop more than goto... What does ednless loop mean? Your program is going to work for the rest of the life? – Viaceslavus Apr 27 '22 at 12:30
2

it is matter of habit, but I prefer:

int returnFlag = FAILURE;

if ((p = malloc...) != NULL)
{
    if ((q = malloc..) != NULL)
    {
        // do some work
        returnFlag = SUCCESS; // success only if it is actually success

        free(q);
    }
    free(p);
}

return returnFlag; // all other variants are failure
Iłya Bursov
  • 23,342
  • 4
  • 33
  • 57
  • 4
    I suggest that you change your habit, because this style leads to the dreadful [arrow-head anti-pattern](http://lostechies.com/chrismissal/2009/05/27/anti-patterns-and-worst-practices-the-arrowhead-anti-pattern/) (see also [this answer](http://programmers.stackexchange.com/a/122625/11110)). – hlovdal Dec 13 '14 at 12:01
  • @hlovdal using of too many resources (which leads to arrow head) is antipattern, for 1-2 ifs it is ok (also total number of lines is important) – Iłya Bursov Dec 13 '14 at 17:25
2

IF you are expecting to allocate a large number of items, it Can get messy. Try to avoid the 'goto' approach. Not because of the old 'goto is bad' ethic, but because that way really can lie madness and memory leaks.

It's a little overkill for small numbers of malloc, but you can consider something like this approach:

void free_mem(void **ptrs, size_t len)
{
    for (size_t i = 0; i < len; ++i)
    {
        free(ptrs[i]);
        ptrs[i] = NULL;
    }
}

int foo(...)
{
    void *to_be_freed[N];
    int next_ptr = 0;
    for (size_t i = 0; i < N; ++i) to_be_freed[i] = NULL;

    p = malloc(..);
    if (!p)
    {
        free_mem(to_be_freed,N);
        return ERROR_CODE;
    }
    to_be_freed[next_ptr++] = p;

    // Wash, rinse, repeat, with other mallocs
    free_mem(to_be_freed,N)
    return SUCCESS;
}

In reality, you can probably wrap malloc with something which tracks this. Put the array and array size in a structure and pass that in with the desired allocation size.

kdopen
  • 8,032
  • 7
  • 44
  • 52
  • 1
    Can you give an example that `goto` will cause troubles here (such as in @abligh's answer)? – Roun Dec 12 '14 at 20:22
  • It won't cause a problem *now*, it will be when someone edits the code in two years' time. – kdopen Dec 12 '14 at 20:24
  • I see, thanks. :) I guess people will have to write their own memory manager eventually if they are working on a large C project. – Roun Dec 12 '14 at 20:30
  • 3
    I strongly disagree that the `goto` approach leads to madness and memory leaks. I've found that the opposite is true: it's the *sane* way to deal with releasing resources in C. As long as you follow a single-entry, single-exit idiom in C with large, complex functions and don't try to buck the trend by explicitly avoiding `goto`, then it keeps things neat and tidy. Future maintenance shouldn't be a problem if using `goto` for cleanup is standard practice. Your approach is fine with `malloc`, but it gets messy if you need to release a mixture of different resource types. – jamesdlin Dec 13 '14 at 10:32
  • But this question was't about other resources. It *was* about malloc. We recently ran coverity across millions of lines of C code. 90% of the resource leaks (unclosed files, memory, etc) were found in functions where the goto approach had been used – kdopen Dec 13 '14 at 19:36
  • Could 90% of your resource leaks have involved `goto` because that's the method 90% of your code used? You're right that `goto` isn't a panacea for being careless. However, people could be careless with any non-automatic method, including this approach of tracking `malloc` calls. (And this method involves more code than than a `goto` approach, which provides opportunity for more mistakes.) – jamesdlin Dec 13 '14 at 20:58
  • Additionally, what do you if your function is expected to return `malloc`ed memory? You explicitly don't want to track that with your memory manager, you might want to free it if your function fails, but then you need to do even more work to untrack a tracked allocation. Ugh. – jamesdlin Dec 13 '14 at 21:01
  • Re the 90%, no the goto approach was not prevalent in the code base. Yes people can write crap in any programming language - and often do. Clearly, if you wanted to return the memory, this would need modifying. And finally, I said "could consider something like" - it was just a suggestion. – kdopen Dec 14 '14 at 06:01