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.