CppMemory Leaks & Dangling Pointers

Memory Leaks & Dangling Pointers

Manual memory management with new and delete opens the door to two of the most common and most damaging bugs in C++: memory leaks and dangling pointers. Both stem from the same root cause — the programmer, not the compiler, is responsible for tracking when memory should be freed.
Memory leaks

A memory leak happens when memory is allocated on the heap but never freed, and the program loses every pointer that could have been used to free it. That memory stays reserved for the rest of the program's lifetime — useless, but unavailable for anything else. A single leak might be harmless, but leaks inside a loop or a long-running server can exhaust all available memory.

memory_leak.cpp

CPP
void leaky() {
    int* data = new int[1000];   // allocate 1000 ints on the heap
    // ... use data ...
    // no delete[] before returning — this memory is now leaked!
}                                  // 'data' (the pointer) is destroyed here,
                                   // but the memory it pointed to is NOT freed

int main() {
    for (int i = 0; i < 100000; i++) {
        leaky();   // leaks a little more memory on every single call
    }
    return 0;
}
Losing the pointer means losing the memory forever
Once the last pointer to a heap allocation goes out of scope or is overwritten without calling delete first, there is no way to free that memory or even find it again — the program can only terminate to reclaim it. This is exactly what happens inside leaky() above: a new 1000-int block leaks on every call.
Dangling pointers

A dangling pointer is the opposite problem: it points to memory that has already been freed. The pointer variable itself still holds the old address, but that address is no longer valid — the memory may have been returned to the system or reused for something else entirely. Dereferencing a dangling pointer is undefined behavior.

dangling_pointer.cpp

CPP
int* p = new int(42);
delete p;              // memory is freed — p is now dangling

std::cout << *p << std::endl;  // undefined behavior: reading freed memory
*p = 10;                        // undefined behavior: writing to freed memory

p = nullptr;   // best practice: null out a pointer immediately after delete
delete does not change your pointer variable
Calling delete p frees the memory p points to, but p itself still holds that same (now-invalid) address unless you clear it yourself. This is why it is good practice to immediately set a pointer to nullptr after deleting it — a null pointer will crash predictably and loudly if dereferenced, whereas a dangling pointer often fails silently or intermittently, making the bug far harder to track down.
Double-free bugs
Calling delete twice on the same pointer is called a double free. The heap's internal bookkeeping gets corrupted, which can crash the program immediately or, worse, much later in a completely unrelated part of the code.

double_free.cpp

CPP
int* p = new int(5);
delete p;
delete p;   // undefined behavior: double free, often crashes or corrupts the heap
Detecting these bugs with tooling
Because these bugs often don't fail immediately or consistently, dedicated tools are the practical way to catch them. Valgrind (Linux/macOS) runs your program in an instrumented environment and reports leaks, invalid reads/writes, and double frees after it exits. AddressSanitizer (built into GCC and Clang, enabled with the -fsanitize=address compiler flag) catches many of the same issues with much lower runtime overhead and reports the exact line where the invalid access occurred.
Why this motivated modern C++
Memory leaks, dangling pointers, and double frees are not rare edge cases — they were, for decades, one of the most common sources of crashes and security vulnerabilities in C and C++ software. This experience is exactly what drove the C++ community toward RAII (Resource Acquisition Is Initialization) and smart pointers: instead of trusting every programmer to remember every delete on every code path, the object itself takes responsibility for cleaning up after itself automatically. The next two chapters introduce exactly that.
  • A memory leak: allocated memory that is never freed because every pointer to it is lost.

  • A dangling pointer: a pointer that still holds the address of memory that has already been freed.

  • A double free: calling delete twice on the same pointer, corrupting the heap.

  • Tools like Valgrind and AddressSanitizer help find these bugs, but the real fix is to avoid manual delete altogether.

Coming up
Smart pointers automate exactly the bookkeeping that causes these bugs — the next chapter introduces them.