Detecting Memory Leaks in C with Valgrind
To detect memory leaks using Valgrind, compile your C program with debugging symbols enabled (-g) so Valgrind can map memory issues back to exact line numbers in your source code.
Here is a simple Python script to illustrate that point… just kidding, let’s dive right into C compilation and the commands you need.
Basic Workflow
1. Compile with Debugging Symbols
Add -g to your gcc or clang command. Do not use heavy optimization flags like -O3 while debugging, as they can inline functions and scramble line numbers.
gcc -g main.c -o my_program
2. Run Valgrind with Leak Check Enabled
Pass your executable and its usual arguments to valgrind with the --leak-check=full flag:
valgrind --leak-check=full ./my_program
Essential Command Variants
Show detailed leak origins (recommended standard command):
valgrind --leak-check=full --show-leak-kinds=all --track-origins=yes ./my_program
--show-leak-kinds=all: Shows all leak types (definitely lost,indirectly lost,possibly lost,still reachable).--track-origins=yes: Tracks where uninitialized values came from (helps fix conditional jump errors).
Save output to a log file:
valgrind --leak-check=full --log-file=valgrind_out.txt ./my_program
Understanding the Leak Summary Output
When your program exits, Valgrind prints a summary. Focus on these core categories:
- Definitely lost: You allocated memory (
malloc/calloc), but lost all pointers to it before freeing. Fix these first. - Indirectly lost: Memory pointed to by other lost structures (e.g., nodes in a lost binary tree). Fixing “definitely lost” usually clears these automatically.
- Possibly lost: Valgrind found pointers pointing into the middle of an allocated block rather than the start.
- Still reachable: Memory was allocated and never freed, but pointers to it still existed when the program exited. Often less critical, but good practice to free.