SyntaxStudy
Sign Up
C++ Memory Leaks, Valgrind, and Address Sanitizer
C++ Beginner 1 min read

Memory Leaks, Valgrind, and Address Sanitizer

A memory leak occurs when heap-allocated memory is no longer reachable but has not been freed. Leaks accumulate over the lifetime of a program, gradually exhausting available memory. They are caused by losing the last pointer to an allocation before calling 'delete', storing raw pointers in containers that get destroyed without cleanup, or exception paths that skip 'delete'. Even small leaks are unacceptable in long-running servers. Valgrind's Memcheck tool instruments a binary at runtime to detect memory errors including leaks, use-after-free, double-free, and use of uninitialised memory. The Address Sanitizer (ASan) is a faster compiler-based alternative available in GCC and Clang via the '-fsanitize=address' flag. ASan has lower overhead than Valgrind and integrates cleanly into continuous integration pipelines. The most effective strategy for eliminating memory management bugs is to avoid manual memory management altogether. Smart pointers — 'std::unique_ptr' and 'std::shared_ptr' — automate cleanup. STL containers own their elements. Following these conventions means that raw 'new' and 'delete' appear only inside the implementation of low-level resource management classes, while all other code remains free of explicit allocation.
Example
#include <iostream>
#include <vector>
#include <memory>

// DEMONSTRATES: what leaks look like and how to fix them

// BAD: leaks on exception path
void leaky(bool throwIt) {
    int* data = new int[1024];
    if (throwIt) throw std::runtime_error("oops");
    delete[] data;   // never reached if exception thrown
}

// GOOD: RAII via vector — never leaks
void safe(bool throwIt) {
    std::vector<int> data(1024);   // owns memory via RAII
    if (throwIt) throw std::runtime_error("oops");
    // data freed automatically whether or not exception thrown
}

// Demonstrates use-after-free (undefined behaviour — don't do this)
void useAfterFree() {
    int* p = new int(42);
    delete p;
    // std::cout << *p;  // UB: reading freed memory
}

int main() {
    // Safe version handles exception cleanly
    try { safe(true); } catch (...) {}

    // Smart pointer — zero manual memory management
    auto buf = std::make_unique<int[]>(1024);
    buf[0] = 99;
    std::cout << "buf[0] = " << buf[0] << "\n";
    // buf freed here automatically

    // Compile with: g++ -fsanitize=address -g -o prog prog.cpp
    // Run: ./prog   (ASan reports any detected errors)

    std::cout << "No leaks with RAII and smart pointers\n";
    return 0;
}