C Language Freeing Memory


Example

It is possible to release dynamically allocated memory by calling free().

int *p = malloc(10 * sizeof *p); /* allocation of memory */
if (p == NULL) 
{
    perror("malloc failed");
    return -1;
}

free(p); /* release of memory */
/* note that after free(p), even using the *value* of the pointer p
   has undefined behavior, until a new value is stored into it. */

/* reusing/re-purposing the pointer itself */
int i = 42;
p = &i; /* This is valid, has defined behaviour */

The memory pointed to by p is reclaimed (either by the libc implementation or by the underlying OS) after the call to free(), so accessing that freed memory block via p will lead to undefined behavior. Pointers that reference memory elements that have been freed are commonly called dangling pointers, and present a security risk. Furthermore, the C standard states that even accessing the value of a dangling pointer has undefined behavior. Note that the pointer p itself can be re-purposed as shown above.

Please note that you can only call free() on pointers that have directly been returned from the malloc(), calloc(), realloc() and aligned_alloc() functions, or where documentation tells you the memory has been allocated that way (functions like strdup () are notable examples). Freeing a pointer that is,

  • obtained by using the & operator on a variable, or
  • in the middle of an allocated block,

is forbidden. Such an error will usually not be diagnosed by your compiler but will lead the program execution in an undefined state.

There are two common strategies to prevent such instances of undefined behavior.

The first and preferable is simple - have p itself cease to exist when it is no longer needed, for example:

if (something_is_needed())
{

    int *p = malloc(10 * sizeof *p);
    if (p == NULL) 
    {
        perror("malloc failed");
        return -1;
    }

    /* do whatever is needed with p */

    free(p);
}

By calling free() directly before the end of the containing block (i.e. the }), p itself ceases to exist. The compiler will give a compilation error on any attempt to use p after that.

A second approach is to also invalidate the pointer itself after releasing the memory to which it points:

free(p);
p = NULL;     // you may also use 0 instead of NULL

Arguments for this approach:

  • On many platforms, an attempt to dereference a null pointer will cause instant crash: Segmentation fault. Here, we get at least a stack trace pointing to the variable that was used after being freed.

    Without setting pointer to NULL we have dangling pointer. The program will very likely still crash, but later, because the memory to which the pointer points will silently be corrupted. Such bugs are difficult to trace because they can result in a call stack that completely unrelated to the initial problem.

    This approach hence follows the fail-fast concept.

  • It is safe to free a null pointer. The C Standard specifies that free(NULL) has no effect:

    The free function causes the space pointed to by ptr to be deallocated, that is, made available for further allocation. If ptr is a null pointer, no action occurs. Otherwise, if the argument does not match a pointer earlier returned by the calloc, malloc, or realloc function, or if the space has been deallocated by a call to free or realloc, the behavior is undefined.

  • Sometimes the first approach cannot be used (e.g. memory is allocated in one function, and deallocated much later in a completely different function)