SyntaxStudy
Sign Up
C Memory Leaks and Valgrind
C Beginner 1 min read

Memory Leaks and Valgrind

A memory leak occurs when a program allocates heap memory and then loses all pointers to it without calling `free()`. The memory remains reserved by the process but is inaccessible to the program. In short-lived command-line tools this is usually harmless because the OS reclaims all memory on exit. In long-running processes — servers, daemons, GUIs — leaks accumulate and eventually exhaust available memory, causing crashes or severe slowdowns. Valgrind is a widely used tool on Linux for detecting memory errors. Running `valgrind --leak-check=full ./my_program` instruments the program, tracks every allocation and free, and reports leaks, double-frees, use-after-free, and reads of uninitialised memory at the end of execution. The output shows the exact call stack at the point of each offending allocation, making leaks easy to locate and fix. Other memory errors include buffer overflow (writing past the end of an array), use-after-free (dereferencing a pointer to freed memory), and stack overflow (too-deep recursion or too-large stack allocations). AddressSanitizer (`-fsanitize=address` with GCC or Clang) is a compiler-based alternative to Valgrind that adds runtime checks with lower overhead and is easy to integrate into automated test pipelines.
Example
/*
 * memory_demo.c — illustrates common memory errors and how
 * to detect them with Valgrind / AddressSanitizer.
 *
 * Compile clean (no errors):
 *   gcc -Wall -Wextra -std=c11 -g memory_demo.c -o memory_demo
 *
 * Check with Valgrind:
 *   valgrind --leak-check=full ./memory_demo
 *
 * Check with AddressSanitizer:
 *   gcc -fsanitize=address -g memory_demo.c -o memory_demo_asan
 *   ./memory_demo_asan
 */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int main(void)
{
    /* --- LEAK EXAMPLE (intentional for demo) --- */
    int *leaked = malloc(64 * sizeof(int));
    /* No free(leaked) — Valgrind will report this */

    /* --- CORRECT allocation/free --- */
    char *msg = malloc(32);
    if (!msg) return EXIT_FAILURE;
    strncpy(msg, "Hello from heap", 31);
    msg[31] = '\0';
    printf("%s\n", msg);
    free(msg);
    msg = NULL;   /* good habit: NULL after free */

    /* --- DOUBLE-FREE (commented out — UB!) --- */
    /* free(msg);  <-- would be double-free since msg is now NULL (safe) */
    /* free(leaked); free(leaked);  <-- actual double-free */

    /* --- USE-AFTER-FREE (commented out — UB!) --- */
    /* free(leaked);  printf("%d\n", leaked[0]);  <-- UB */

    /* Clean up the leaked pointer so the demo actually runs clean */
    free(leaked);

    printf("Done. Run under valgrind for memory analysis.\n");
    return EXIT_SUCCESS;
}