Home:ALL Converter>How to free previously allocated memory when errors occured?

How to free previously allocated memory when errors occured?

Ask Time:2011-09-21T04:35:24         Author:Max

Json Formatter

Given a function declaration like this:

int base_address(zval *object, int add_prefix, char **base_address TSRMLS_DC) {    
    int result;

    char *host;
    long port;
    char *prefix;  

    host = ... get host from object ...;
    port = ... get port from object ...;
    prefix = ... get prefix from object ...;

    result = SUCCESS;

    if (asprintf(base_address, "%s:%ld/%s", host, port, prefix) < 0) {
        result = FAILURE;
    }

    return result;
}

void my_func() {
    char *base_address;
    char *ping_url;

    if (base_address(getThis(), 0, &base_address TSRMLS_CC) == FAILURE) {
        MALLOC_ERROR();
    }

    if (asprintf(&ping_url, "%s/ping", base_address) < 0) {
        MALLOC_ERROR();
    }

   ... do some stuff with base address ...

    // release both, as everything worked
    free(base_address);
    free(ping_url);
}

If the first call to base_address succeeded and the second call to asprintf() failed, how do I cleanly skip to the end of the function in order to safely release allocated memory?

Is there some standard pattern how to avoid memory leaks in these situations where memory is allocated one after another (and each allocation might fail) without too much code duplication or goto statements?

Author:Max,eproduced under the CC 4.0 BY-SA copyright license with a link to the original source and this disclaimer.
Link to original article:https://stackoverflow.com/questions/7491441/how-to-free-previously-allocated-memory-when-errors-occured
Jon Purdy :

Don't be afraid of goto. It's the simplest, cleanest, and most legible way of handling exceptions in C:\n\n\nYou don't repeat yourself. Duplicated code is error-prone.\nYou don't create deeply nested code. Deep nesting is illegible.\nYou don't hide behind do {...} while (0) and break. Good code says what it means.\n\n\nHere's a basic example:\n\nint operation() {\n\n int result = SUCCESS;\n\n if ((result = may_fail_first()) == FAILURE) {\n goto failed_first;\n }\n\n if ((result = may_fail_second()) == FAILURE) {\n goto failed_second;\n }\n\n // If your cleanup code doesn't ordinarily need to run.\n goto end;\n\nfailed_second:\n cleanup_second();\n\n // If you don't need to clean up everything.\n goto end;\n\nfailed_first:\n cleanup_first();\n\nend:\n return result;\n\n}\n",
2011-09-20T20:44:57
Blagovest Buyukliev :

This is one of the sane usages of goto for error handling:\n\nif (base_address(getThis(), 0, &base_address TSRMLS_CC) == FAILURE) {\n goto end;\n}\n\nif (asprintf(&ping_url, \"%s/ping\", base_address) < 0) {\n goto release_address;\n}\n\n// do stuff\n\nrelease_address:\nfree(base_address);\nend:\n\n\nThis way you don't have to repeat the same releasing code in case you have many allocating calls which depend on each other.\n\nYou may want to refer to another of my answers here, which talks about the general case.",
2011-09-20T20:44:29
user166390 :

If NULL is assigned to the pointer variables during declaration then free will be able to handle the cases where they were never malloced as well (it will simply do nothing). (Other guards can be used, such as -1 to refer to an invalid fd, for instance.)\n\nI use this in conjunction with with the use goto for (additional) cleanup -- some of the other answers look far to \"wordy\" to me. I find that goto has to be used judiciously to avoid spaghetti-code and, in general, I find \"gotos around gotos\" just too hard to keep track of consistently. The additional assignment of NULL first also allows the variables themselves to be used as check-guards (during any possible cleanup or otherwise).\n\nHappy coding.\n\n\n\nExample:\n\nvoid my_func() {\n char *base_address = NULL;\n char *ping_url = NULL;\n\n if (base_address(getThis(), 0, &base_address TSRMLS_CC) == FAILURE) {\n goto cleanup;\n }\n\n if (asprintf(&ping_url, \"%s/ping\", base_address) < 0) {\n goto cleanup;\n }\n\n // stuff... and assign return value (or return directly) if applicable,\n // assign NULL to variables which contain values that should\n // not be free'd, etc (use as guards!).\n // I prefer to add guards into the cleanup vs. \"skip over it\".\n // I vary rarely, if ever, have multiple cleanup sections -- in most\n // cases this would indicate the need for additional function(s) to me.\n\n cleanup:\n free(base_address);\n if (ping_url) {\n // perhaps need additional cleanup \n free(ping_url);\n }\n\n // Return value, if applicable. (If a \"direct return\" above isn't\n // advisable for the given function due to cleanup rules.)\n}\n",
2011-09-20T20:52:31
yy