Table of Contents

Valgrind memcheck

memcheck tracks every byte of memory and detects four main classes of error: invalid memory access (reading/writing past buffer bounds), use-after-free, uninitialized value usage, and memory leaks. It maintains shadow state for every allocation—whether each byte is initialized and whether it's still allocated.

valgrind ./program
valgrind --leak-check=full ./program
valgrind --leak-check=full --track-origins=yes ./program

Without flags, memcheck only reports invalid accesses and leaks. --leak-check=full provides detailed leak information including allocation stack traces. --track-origins=yes tracks where uninitialized values came from (adds overhead but invaluable when debugging uninitialized value errors).

Invalid memory access occurs when reading or writing outside an allocation's bounds:

int arr[10];
arr[15] = 5;           // write past end of buffer
int x = arr[100];      // read past end

memcheck catches this instantly and reports: “Invalid write/read of size N at address X, 0 bytes after a block of size M allocated at…”. The report includes the allocation stack trace, making it easy to find where the buggy buffer came from.

Use-after-free occurs when accessing memory that's been freed:

int *p = malloc(sizeof(int));
free(p);
*p = 10;               // use after free

memcheck reports: “Invalid write of size 4 at address X, which is 0 bytes inside a block of size 4 free'd at… previously allocated at…”. Both the free and allocation stack traces are shown.

Uninitialized values occur when reading memory before writing to it:

int x;
printf("%d\n", x);     // x is uninitialized

memcheck reports: “Use of uninitialised value of size 4”. With --track-origins=yes, it shows where the uninitialized value came from (usually from a malloc or stack allocation).

Memory leaks occur when allocated memory is never freed. Leak checking modes:

--leak-check=no         # don't check for leaks
--leak-check=summary    # report only summary (total leaked bytes)
--leak-check=full       # report each leaked block
--leak-check=reachable  # also show still-reachable blocks

Leak kinds:

Report only definitely and indirectly lost by default. Use --show-leak-kinds=all to see everything.

Leak summary example:

==12345== LEAK SUMMARY:
==12345==    definitely lost: 42 bytes in 3 blocks
==12345==    indirectly lost: 0 bytes in 0 blocks
==12345==      possibly lost: 10 bytes in 1 block
==12345==    still reachable: 100 bytes in 5 blocks

Focus on “definitely lost”—those are real leaks. “Still reachable” is usually acceptable; it's memory allocated at startup and kept alive for the program's lifetime.