student@ubuntu:~$
pointers-memory Lesson 9 9 min read

Debugging Memory with Valgrind

Find leaks, invalid reads, and use-after-free bugs before they find you

Reading: Supplemental (not in textbook)

Quick check before you start: Have you ever had a segfault and no idea where it came from? If yes, Valgrind is about to become your best friend. If you already use Valgrind, skip to Reading the Output.

Practice this topic: Valgrind skill drill

After this lesson, you will be able to:

  • Compile with -g for debug symbols
  • Run Valgrind with --leak-check=full
  • Interpret “definitely lost,” “indirectly lost,” and “invalid read” messages
  • Map Valgrind errors back to specific source lines

Step 1: Compile with Debug Symbols

Valgrind can only show you file names and line numbers if your binary contains debug info. Add -g to your gcc command:

gcc -Wall -Wextra -g -o program program.c

Without -g, Valgrind still detects errors, but the output says things like at 0x4005F2 instead of at program.c:17. That is useless for debugging.

Step 2: Run Valgrind

valgrind --leak-check=full ./program

Valgrind runs your program inside a virtual CPU. It is about 20x slower than normal execution, but it tracks every byte of memory.

Your program runs normally — you type input, see output. But after your program exits, Valgrind prints a report.

Reading the Output

A clean report looks like this:

==12345== HEAP SUMMARY:
==12345==   in use at exit: 0 bytes in 0 blocks
==12345== All heap blocks were freed -- no leaks are possible
==12345== ERROR SUMMARY: 0 errors from 0 contexts

Zero bytes in use at exit. Zero errors. That is your goal for every program.

Leak Categories

When leaks exist, Valgrind classifies them:

Category Meaning
Definitely lost You allocated memory and lost all pointers to it. This is a real leak. Fix it.
Indirectly lost Memory reachable only through a “definitely lost” block. Fix the parent leak and this goes away.
Possibly lost Valgrind is not sure. Rare in simple programs. Investigate.
Still reachable You still have a pointer to it at exit, but never freed it. Usually a minor cleanup issue.

Invalid Reads and Writes

==12345== Invalid read of size 4
==12345==    at 0x4005F2: main (program.c:17)
==12345==  Address 0x5205048 is 8 bytes inside a block of size 4 free'd
==12345==    at 0x4C2ACBD: free (vg_replace_malloc.c:530)
==12345==    by 0x4005E8: main (program.c:15)

This tells you: on line 17, you read 4 bytes from memory that was freed on line 15. That is a use-after-free bug.

Mapping Errors to Source Lines

Valgrind gives you a call stack for every error. Read it bottom to top:

  1. The top frame is where the error happened
  2. Lower frames show how you got there
  3. The file name and line number point you directly to the bug
==12345== 40 bytes in 1 blocks are definitely lost
==12345==    at 0x4C2BBAF: calloc (vg_replace_malloc.c:711)
==12345==    by 0x400597: build_array (program.c:8)
==12345==    by 0x4005C1: main (program.c:22)

Translation: main called build_array on line 22. build_array called calloc on line 8. That allocation was never freed.

A Complete Example

#include <stdio.h>
#include <stdlib.h>

int main(void) {
    int *arr = calloc(10, sizeof(int));
    if (arr == NULL) { return 1; }

    arr[0] = 42;
    printf("%d\n", arr[0]);

    free(arr);
    arr = NULL;
    return 0;
}

Compile and run:

gcc -Wall -Wextra -g -o demo demo.c
valgrind --leak-check=full ./demo

Result: zero leaks, zero errors.


Check Your Understanding
In Valgrind output, what does "definitely lost" mean?
AMemory was corrupted by an invalid write
BMemory was freed twice
CMemory was allocated but all pointers to it were lost without freeing
DMemory was accessed after the program exited
Answer: C. "Definitely lost" means Valgrind found a heap allocation for which no pointer exists at program exit. You allocated the memory, then either overwritten or let the pointer go out of scope without calling free. This is a confirmed memory leak.

What Comes Next

You have the tools to manage memory correctly. Next week, you will learn to group related data together using structs — C’s answer to Java classes.

Next: Structs & typedef