Memory Leaks
C gives you full control over dynamic memory: you decide exactly when to allocate it and exactly when to give it back with free. That control is also the risk — if you lose the only pointer to a block of allocated memory before freeing it, that memory can never be freed again for the rest of the program's run. This is a memory leak.
What a leak actually is
A leak has two ingredients: memory that was allocated (with malloc, calloc, or realloc), and the loss of every pointer that referred to it, before free was called. Once that happens, the memory is still marked "in use" from the operating system's point of view, but your program has no way left to reach it or free it.
#include <stdlib.h>
void leaky_function(void) {
int *data = malloc(100 * sizeof(int));
// ... use data ...
// BUG: function returns without calling free(data).
// data was the only pointer to that block, and it just went out
// of scope. The 400 bytes it pointed to are now unreachable.
}
int main(void) {
for (int i = 0; i < 1000; i++) {
leaky_function(); // leaks 400 bytes every single call
}
return 0;
}Why leaks matter more in long-running programs
A short command-line tool that leaks a few bytes and then exits is harmless in practice — the operating system reclaims all of a process's memory when it terminates. The real danger is in long-running programs: servers, daemons, games, embedded systems, anything that stays alive for hours, days, or months. In those programs, even a small leak that happens repeatedly (once per request, once per frame, once per loop iteration) steadily grows the process's memory footprint until it exhausts available memory, causing severe slowdowns or crashes far from where the actual bug is.
Finding leaks with Valgrind
Leaks are notoriously hard to spot by reading code alone, because the bug is an absence — a free that should exist but does not. Tools like Valgrind's memcheck run your compiled program and track every allocation and free at runtime, then report exactly which allocations were never freed, along with the call stack that created them. Running a C program under Valgrind periodically during development is one of the most effective habits for catching leaks early, and is well worth learning as a follow-up to this page.
valgrind --leak-check=full ./my_program
==12345== HEAP SUMMARY: ==12345== in use at exit: 400 bytes in 1 blocks ==12345== LEAK SUMMARY: ==12345== definitely lost: 400 bytes in 1 blocks ==12345== at 0x...: malloc ==12345== by 0x...: leaky_function
Fixing the leak
The fix is simply to free the memory before the last pointer to it disappears:
#include <stdlib.h>
void not_leaky(void) {
int *data = malloc(100 * sizeof(int));
// ... use data ...
free(data); // give the memory back before data goes out of scope
}A leak happens when allocated memory is never freed because every pointer to it is lost.
Leaks do not crash immediately — they accumulate silently, which is what makes them dangerous in long-running programs.
Valgrind's memcheck can run a program and report exactly which allocations were never freed.
Plan the matching
freefor everymallocbefore writing the allocation, including on early-return error paths.