CFreeing Memory with free()

Freeing Memory with free()

Every block of memory you obtain from malloc(), calloc(), or realloc() comes from a shared pool called the heap, and C does not clean that pool up for you. When you are finished with a block, you must explicitly return it to the system with free(), declared in <stdlib.h>.

C
void free(void *ptr);
The rule is simple to state and surprisingly easy to get wrong in practice: every successful allocation must eventually be matched with exactly one call to free() — not zero calls (a leak), and not two or more calls (a double free).

C
#include <stdio.h>
#include <stdlib.h>

int main(void) {
    int *arr = malloc(5 * sizeof(int));

    if (arr == NULL) {
        return 1;
    }

    for (int i = 0; i < 5; i++) {
        arr[i] = i;
    }

    // ... use arr ...

    free(arr); // exactly one matching free for the one malloc above
    return 0;
}
Double Free: Calling free() Twice
Calling free() more than once on the same pointer is undefined behavior. It typically corrupts the heap's internal bookkeeping structures, which can cause a crash immediately, a crash much later in an unrelated part of the program, or — in the worst case — an exploitable security vulnerability.
Double free

C
int *p = malloc(sizeof(int));
free(p);
free(p); // UNDEFINED BEHAVIOR: 'p' was already freed once
The heap allocator has no way to know whether the memory pointed to by p has already been returned to the pool. Freeing it a second time corrupts internal allocator state, and by the time the crash actually happens, the real bug may be far away in the code and very hard to trace back.
Use-After-Free: Using Memory After Freeing It
Once you call free() on a block, that memory no longer belongs to you. The allocator is free to reuse it for a completely different allocation elsewhere in your program at any time. Reading from or writing to it afterward is undefined behavior, commonly called a use-after-free.
Use-after-free

C
int *p = malloc(sizeof(int));
*p = 42;
free(p);

printf("%d\n", *p); // UNDEFINED BEHAVIOR: reading freed memory
*p = 100;             // UNDEFINED BEHAVIOR: writing to freed memory
The program might appear to "work" because the memory hasn't been reused yet — which is exactly what makes use-after-free bugs so dangerous. They can pass testing and then fail unpredictably in production once memory reuse patterns change.
Good Practice: Set the Pointer to NULL After Freeing
A simple habit prevents a large fraction of double-free and use-after-free bugs: immediately after calling free(), set the pointer to NULL.

C
int *p = malloc(sizeof(int));
*p = 42;

free(p);
p = NULL; // now 'p' can no longer be accidentally reused

// A later "free(p);" here is now harmless -- free(NULL) is well-defined
// and simply does nothing.

// A later "*p = 5;" here would crash immediately and obviously (NULL
// dereference) instead of silently corrupting memory -- much easier to debug.
free(NULL) is explicitly safe
The C standard guarantees that calling free() with a NULL pointer is a well-defined no-op. This is precisely what makes the "set to NULL after freeing" habit effective: it turns a dangerous double free into a harmless, do-nothing call.
Think of every malloc/free as a matched pair
When writing or reviewing C code, it helps to mentally (or literally, in comments) pair up every allocation with the exact spot where it gets freed. If you cannot immediately answer "where does this get freed?" for a given allocation, that is a strong signal of a potential leak or an unclear ownership design.
  • Every successful malloc/calloc/realloc must eventually be paired with exactly one free() call

  • Calling free() twice on the same pointer is a double free -- undefined behavior that can corrupt the heap

  • Reading or writing memory after it has been freed is a use-after-free -- also undefined behavior

  • Set a pointer to NULL immediately after freeing it -- free(NULL) is guaranteed safe and a later NULL dereference fails loudly and obviously