devnotes.

Avoiding Memory Leaks and Dangling Pointers

Manual memory management is powerful but introduces two classic categories of bugs that don't exist in garbage-collected languages.

// Memory leak: allocated memory that's never freed
void leaky(void) {
    int *data = malloc(100 * sizeof(int));
    // ... use data ...
    // forgot free(data); — this memory is now unreachable and unrecoverable
}

// Dangling pointer: using memory after it's been freed
int *danger = malloc(sizeof(int));
*danger = 42;
free(danger);
// *danger = 10;      // ❌ undefined behavior — reading/writing freed memory
danger = NULL;         // ✅ good practice: set to NULL (or nullptr in C23) immediately after free

// Double free: freeing the same pointer twice
free(danger);          // ✅ safe — freeing NULL is explicitly defined to do nothing

A memory leak happens when allocated memory loses its last reachable pointer without ever being freed — the program can no longer find it to release it, so it stays reserved until the program exits, and a long-running program (a server, for example) can slowly exhaust all available memory this way.

A dangling pointer is a pointer that still holds the address of memory that has already been freed. Using it — reading, writing, or even passing it to another function — is undefined behavior: it might crash immediately, silently corrupt unrelated data, or appear to work fine until a much later, harder-to-diagnose failure. Setting a pointer to NULL right after free()-ing it is the standard defense, since dereferencing NULL fails loudly and immediately rather than corrupting memory silently, and free(NULL) is explicitly guaranteed to be a safe no-op.

Tools like Valgrind (Linux/macOS) and AddressSanitizer (-fsanitize=address, built into GCC and Clang) detect leaks, dangling-pointer use, and buffer overflows automatically during testing — running your test suite under one of these regularly is one of the highest-value habits in C development.

Avoiding Memory Leaks and Dangling Pointers in C | DevNotes