E Robertson
E Robertson

Reputation: 43

C cleaning up on error/termination

I run a bunch of mallocs in sequence, and check each time to make sure it was successful. Something like this:

typedef struct {
    int *aray; 
    char *string;
} mystruct;

mystruct *mystruct_init(int length)
{
    mystruct *foo = malloc(sizeof(int *));
    if (!foo) exit(1);

    foo->array = malloc(length * sizeof(int));
    if (!foo->array) exit(1);

    foo->string = malloc(length * sizeof(char));
    if (!foo->string) exit(1);

    return foo;
}

So when a malloc fails, the program exits without freeing the previous ones. What are some techniques to make sure that in case something fails, the program exits safely with all the allocated memory freed?

Upvotes: 2

Views: 113

Answers (5)

Iharob Al Asimi
Iharob Al Asimi

Reputation: 53016

You can do it this way too, I think it's the cleanest way to do it and I use it very often, but to return from a function, not to exit from the program since it is true that system will free memory at exit.

By the way your program as is will give segmentation fault, since you are not allocating enough space for foo.

typedef struct {
    int *aray; 
    char *string;
} mystruct;

mystruct *mystruct_init(int length)
{
    mystruct *foo = malloc(sizeof(mystruct)); // you need to allocate space for string too
    if (!foo)
        return NULL;
    memset(foo, 0, sizeof(mystruct)); // initialize all members to NULL

    foo->array = malloc(length * sizeof(int));
    if (!foo->array)
        goto abort;

    foo->string = malloc(length * sizeof(char));
    if (!foo->string)
        goto abort;

    return foo;
abort:
    if (foo->array)
        free(foo->array);
    if (foo->string)
        free(foo->string);
    return NULL;
}

now in the calling function you can do it cleanly like

mystruct *foo;

foo = mystruct_init(length /* assuming this is declared and set. */);
if (!foo)
    exit(1);

Upvotes: 0

webbertiger
webbertiger

Reputation: 809

If you're designing a driver that runs as part of the OS, you'll need to take care of this more carefully. In this case, goto is often used. For example

mystruct *mystruct_init(int length)
{
    mystruct *foo = malloc(sizeof(int *));
    if (!foo) goto FOO_FAIL;

    foo->array = malloc(length * sizeof(int));
    if (!foo->array) goto ARRAY_FAIL;

    foo->string = malloc(length * sizeof(char));
    if (!foo->string) goto STRING_FAIL;

    return foo;

    STRING_FAIL:
        free(foo->array);
    ARRAY_FAIL:
        free(foo);
    FOO_FAIL:
        REPORT_ERROR; // user defined behavior
    return NULL;
}

So if, say, foo->string is not allocated successfully, foo->array and foo will be freed accordingly. If foo->array fails, only foo will be freed. When any of these fails, the function returns NULL, which allows the caller to check the return value and decide next step.

Upvotes: 2

pm100
pm100

Reputation: 50190

if you want to do post-mortem cleanup then you can register an atexit handler

http://man7.org/linux/man-pages/man3/atexit.3.html

BTW I am not saying its easy to do (or correct)

Upvotes: 1

pts
pts

Reputation: 87311

Most modern operating systems would free the right memory after the process terminates after calling exit(...). These operating systems include:

  • all Unix variants, including Linux and Mac OS X
  • all Windows versions
  • all DOS variants

Upvotes: 3

Lee Daniel Crocker
Lee Daniel Crocker

Reputation: 13171

Here's one of my favorite tricks:

do {
    a = malloc(...);
    if (!a) break;

    b = malloc(...);
    if (!b) {
        free(a);
        break;
    }
} while (0);

if (!a) {
    ...
    return;
}

...

free(b);
free(a);

Upvotes: 2

Related Questions