Memory leaks, dangling pointers & double free

RoadmapsC++

Scenario

Your server application processes thousands of requests per second. It runs perfectly for 3 hours, then abruptly crashes with an 'Out of Memory' (OOM) kill signal from the operating system.

If your program logic never changed, why did it suddenly run out of memory?

Mental model

A Dangling Pointer is having a map to a house that was already demolished. A Memory Leak is losing your map to a house you own, so you can never sell it. A Double Free is hiring a demolition crew to demolish an empty lot that was already demolished yesterday.

Manual memory management in C++ forces the programmer to be a perfect accountant. Failing to balance the books results in three classic bugs: Memory Leaks (failing to free memory), Dangling Pointers (accessing memory after it was freed), and Double Free (freeing memory twice).

Explanation

When working with the Heap, the compiler does not help you clean up. You are entirely responsible for the lifecycle of the memory.

**1. Memory Leaks**

If you allocate memory using `new`, but the pointer holding the address goes out of scope before you call `delete`, that memory is lost forever. It is 'leaked'. The OS marks it as in-use, but your program has no way to access it. Over time, leaks accumulate until the OS kills your program.

**2. Dangling Pointers**

When you call `delete ptr;`, the memory on the heap is returned to the OS. However, the variable `ptr` itself still holds the old memory address. If you try to dereference `ptr` later (`*ptr = 5`), you are writing to memory you no longer own. This causes data corruption, security vulnerabilities, or segmentation faults.

**3. Double Free**

If you delete the same pointer twice, you corrupt the heap manager's internal bookkeeping. The program will almost always crash immediately.

Code examples

The Big Three Memory Bugs

void memoryBugs() {
    // 1. MEMORY LEAK
    int* leakPtr = new int(10);
    // We reassign the pointer, losing the only reference to '10'
    leakPtr = new int(20); 
    
    // 2. DANGLING POINTER
    int* danglingPtr = new int(30);
    delete danglingPtr; // Memory is returned to the OS
    // BUG: The pointer still holds the address. Dereferencing is UB.
    // *danglingPtr = 40; 
    
    // 3. DOUBLE FREE
    int* dfPtr = new int(50);
    delete dfPtr;
    // BUG: Deleting again corrupts the heap manager.
    // delete dfPtr;
}

These bugs often happen across complex codebases where ownership isn't clear. To mitigate dangling pointers, a common practice in legacy code is setting the pointer to `nullptr` immediately after deletion, as deleting a `nullptr` is a safe no-op.

Key points

Common mistakes

Recall questions

Questions & answers

A C++ program uses a tree structure. The developer writes a manual `cleanupTree(Node* root)` function that calls `delete` on the left and right children recursively. However, multiple nodes can share the same child in this specific graph. What bug will occur?

A Double Free (or Multiple Free). When the first parent is cleaned up, it deletes the shared child. When the second parent is cleaned up, it attempts to `delete` the exact same child pointer again, corrupting the heap and crashing.

Approach: Recognize that shared ownership with raw `delete` inevitably leads to double frees.

A developer returns a pointer to a local variable `int x = 5;`. When the caller tries to print it, it outputs garbage data. Why?

The local variable `x` was allocated on the stack and destroyed when the function returned. The returned pointer is a dangling pointer pointing to dead memory that has likely been overwritten.

Approach: Identify dangling pointers to stack memory.

Continue learning

Previous: Stack vs heap — new/delete & memory layout

Next: RAII — resource acquisition is initialization

Return to C++ Roadmap