Should one really set pointers to `NULL` after freeing them? Should one really set pointers to `NULL` after freeing them? c c

Should one really set pointers to `NULL` after freeing them?


The second one is way more important: re-using a freed pointer can be a subtle error. Your code keeps right on working, and then crashes for no clear reason because some seemingly unrelated code wrote in the memory that the re-used pointer happens to be pointing at.

I once had to work on a really buggy program someone else wrote. My instincts told me that many of the bugs were related to sloppy attempts to keep using pointers after freeing the memory; I modified the code to set the pointers to NULL after freeing the memory, and bam, the null pointer exceptions started coming. After I fixed all the null pointer exceptions, suddenly the code was much more stable.

In my own code, I only call my own function that is a wrapper around free(). It takes a pointer-to-a-pointer, and nulls the pointer after freeing the memory. And before it calls free, it calls Assert(p != NULL); so it still catches attempts to double-free the same pointer.

My code does other things too, such as (in the DEBUG build only) filling memory with an obvious value immediately after allocating it, doing the same right before calling free() in case there is a copy of the pointer, etc. Details here.

EDIT: per a request, here is example code.

voidFreeAnything(void **pp){    void *p;    AssertWithMessage(pp != NULL, "need pointer-to-pointer, got null value");    if (!pp)        return;    p = *pp;    AssertWithMessage(p != NULL, "attempt to free a null pointer");    if (!p)        return;    free(p);    *pp = NULL;}// FOO is a typedef for a struct typevoidFreeInstanceOfFoo(FOO **pp){    FOO *p;    AssertWithMessage(pp != NULL, "need pointer-to-pointer, got null value");    if (!pp)        return;    p = *pp;    AssertWithMessage(p != NULL, "attempt to free a null FOO pointer");    if (!p)        return;    AssertWithMessage(p->signature == FOO_SIG, "bad signature... is this really a FOO instance?");    // free resources held by FOO instance    if (p->storage_buffer)        FreeAnything(&p->storage_buffer);    if (p->other_resource)        FreeAnything(&p->other_resource);    // free FOO instance itself    free(p);    *pp = NULL;}

Comments:

You can see in the second function that I need to check the two resource pointers to see if they are not null, and then call FreeAnything(). This is because of the assert() that will complain about a null pointer. I have that assert in order to detect an attempt to double-free, but I don't think it has actually caught many bugs for me; if you want to leave out the asserts, then you can leave out the check and just always call FreeAnything(). Other than the assert, nothing bad happens when you try to free a null pointer with FreeAnything() because it checks the pointer and just returns if it was already null.

My actual function names are rather more terse, but I tried to pick self-documenting names for this example. Also, in my actual code, I have debug-only code that fills buffers with the value 0xDC before calling free() so that if I have an extra pointer to that same memory (one that doesn't get nulled out) it becomes really obvious that the data it's pointing to is bogus data. I have a macro, DEBUG_ONLY(), which compiles to nothing on a non-debug build; and a macro FILL() that does a sizeof() on a struct. These two work equally well: sizeof(FOO) or sizeof(*pfoo). So here is the FILL() macro:

#define FILL(p, b) \    (memset((p), b, sizeof(*(p)))

Here's an example of using FILL() to put the 0xDC values in before calling:

if (p->storage_buffer){    DEBUG_ONLY(FILL(pfoo->storage_buffer, 0xDC);)    FreeAnything(&p->storage_buffer);}

An example of using this:

PFOO pfoo = ConstructNewInstanceOfFoo(arg0, arg1, arg2);DoSomethingWithFooInstance(pfoo);FreeInstanceOfFoo(&pfoo);assert(pfoo == NULL); // FreeInstanceOfFoo() nulled the pointer so this never fires


I don't do this. I don't particularly remember any bugs that would have been easier to deal with if I did. But it really depends on how you write your code. There are approximately three situations where I free anything:

  • When the pointer holding it is about to go out of scope, or is part of an object which is about to go out of scope or be freed.
  • When I am replacing the object with a new one (as with reallocation, for instance).
  • When I am releasing an object which is optionally present.

In the third case, you set the pointer to NULL. That's not specifically because you're freeing it, it's because the whatever-it-is is optional, so of course NULL is a special value meaning "I haven't got one".

In the first two cases, setting the pointer to NULL seems to me to be busy work with no particular purpose:

int doSomework() {    char *working_space = malloc(400*1000);    // lots of work    free(working_space);    working_space = NULL; // wtf? In case someone has a reference to my stack?    return result;}int doSomework2() {    char * const working_space = malloc(400*1000);    // lots of work    free(working_space);    working_space = NULL; // doesn't even compile, bad luck    return result;}void freeTree(node_type *node) {    for (int i = 0; i < node->numchildren; ++i) {        freeTree(node->children[i]);        node->children[i] = NULL; // stop wasting my time with this rubbish    }    free(node->children);    node->children = NULL; // who even still has a pointer to node?    // Should we do node->numchildren = 0 too, to keep    // our non-existent struct in a consistent state?    // After all, numchildren could be big enough    // to make NULL[numchildren-1] dereferencable,    // in which case we won't get our vital crash.    // But if we do set numchildren = 0, then we won't    // catch people iterating over our children after we're freed,    // because they won't ever dereference children.    // Apparently we're doomed. Maybe we should just not use    // objects after they're freed? Seems extreme!    free(node);}int replace(type **thing, size_t size) {    type *newthing = copyAndExpand(*thing, size);    if (newthing == NULL) return -1;    free(*thing);    *thing = NULL; // seriously? Always NULL after freeing?    *thing = newthing;    return 0;}

It's true that NULL-ing the pointer can make it more obvious if you have a bug where you try to dereference it after freeing. Dereferencing probably does no immediate harm if you don't NULL the pointer, but is wrong in the long run.

It's also true that NULL-ing the pointer obscures bugs where you double-free. The second free does no immediate harm if you do NULL the pointer, but is wrong in the long run (because it betrays the fact that your object lifecycles are broken). You can assert things are non-null when you free them, but that results in the following code to free a struct which holds an optional value:

if (thing->cached != NULL) {    assert(thing->cached != NULL);    free(thing->cached);    thing->cached = NULL;}free(thing);

What that code tells you, is that you've got in too far. It should be:

free(thing->cached);free(thing);

I say, NULL the pointer if it's supposed to remain usable. If it isn't usable any more, best not to make it falsely appear to be, by putting in a potentially-meaningful value like NULL. If you want to provoke a page fault, use a platform-dependent value which isn't dereferancable, but which the rest of your code won't treat as a special "everything is fine and dandy" value:

free(thing->cached);thing->cached = (void*)(0xFEFEFEFE);

If you can't find any such constant on your system, you may be able to allocate a non-readable and/or non-writeable page, and use the address of that.


If you don't set the pointer to NULL there is a not-so-small chance, that your application continues to run in an undefined state and crashes later on at a completely unrelated point. Then you will spend a lot of time with debugging a nonexistent error before you find out, that it's a memory-corruption from earlier.

I'd set the pointer to NULL because the chances are higher that you'll hit the correct spot of the error earlier than if you didn't set it to NULL. The logical error of freeing memory a second time is still to be thought of and the error that your application does NOT crash on null-pointer access with a large enough offset is in my opinion completely academic although not impossible.

Conclusion: I'd go for setting the pointer to NULL.