0Pricing
C Academy · Lesson

Freeing and Avoiding Leaks

Clean up correctly.

Freeing and Avoiding Leaks is a free C Academy lesson on CoddyKit — lesson 4 of 4. You can read the complete lesson below for free — then practise it hands-on in the browser with a built-in code editor and a 24/7 AI tutor. It is part of the C Academy learning path, one of 4 lessons in the course, and your progress syncs across the web and the CoddyKit app.

Every malloc Needs a free

Heap memory stays reserved until you release it with free. If you lose the only pointer to a block without freeing it, that memory is leaked.

Leaks pile up over time and can exhaust memory in long-running programs.

int *a = malloc(100 * sizeof(*a));
/* ... use a ... */
free(a);

What free Actually Does

free(p) returns the block to the allocator so it can be reused. It does not change the value of p itself.

After free, p still points at the old address, which is now invalid. Touching it is undefined behavior.

free(p);
/* p is now a dangling pointer */

The Dangling Pointer Trap

Using a pointer after freeing it is a use-after-free bug. It may seem to work, then corrupt data or crash unpredictably.

A simple guard is to set the pointer to NULL right after freeing.

free(a);
a = NULL;   /* now misuse is a clean NULL deref, easier to catch */

Double Free Is Undefined

Calling free twice on the same block corrupts the allocator's internal state and often crashes.

Setting the pointer to NULL after the first free helps, because free(NULL) is explicitly safe and does nothing.

free(a);
a = NULL;
free(a);   /* free(NULL) is a harmless no-op */

Free Only What You malloc'd

Pass free only a pointer that came from malloc, calloc, or realloc.

Freeing a stack variable's address, a string literal, or a pointer into the middle of a block is undefined behavior.

int x = 5;
/* free(&x);   WRONG: x is on the stack */
int *p = malloc(sizeof(int));
free(p);        /* correct */

Leak From an Early Return

A common leak: you allocate, then hit an error path that returns before freeing. The block is lost.

Make sure every exit path that owns the memory frees it first.

int *a = malloc(n * sizeof(*a));
if (something_failed) {
    free(a);   /* don't forget this before returning */
    return -1;
}

A Clean Allocate-and-Free Program

This program allocates, uses, and frees memory along every path, leaking nothing.

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

int main(void) {
    int n = 4;
    int *a = malloc(n * sizeof(*a));
    if (!a) return 1;
    for (int i = 0; i < n; i++) a[i] = i + 1;
    int sum = 0;
    for (int i = 0; i < n; i++) sum += a[i];
    printf("%d\n", sum);
    free(a);
    a = NULL;
    return 0;
}

Freeing Nested Allocations

If a block contains pointers to other blocks, free the inner ones first, then the outer.

Freeing the outer block first would lose the addresses of the inner blocks and leak them.

for (size_t i = 0; i < rows; i++)
    free(grid[i]);   /* free each row first */
free(grid);          /* then the array of pointers */

One Owner per Block

Decide which part of your code owns each allocation and is responsible for freeing it.

If two pointers alias the same block and both free it, you get a double free. Clear ownership prevents that.

Detecting Leaks with Tools

You don't have to find leaks by eye. Tools like valgrind or AddressSanitizer report leaks and invalid frees with line numbers.

Compile with -g and run under the tool to see exactly what wasn't freed.

/* gcc -g -fsanitize=address prog.c && ./a.out */
/* or: valgrind --leak-check=full ./a.out */

Free in Reverse Order of Setup

A reliable habit: in a function that acquires several resources, release them in the reverse order you acquired them.

This mirrors how the dependencies were built and keeps cleanup predictable.

char *buf = malloc(64);
int  *idx = malloc(64 * sizeof(int));
/* ... use them ... */
free(idx);   /* free last-acquired first */
free(buf);

Quick Check

Test your understanding of freeing memory.

Recap

Pair every allocation with exactly one free, on every code path.

Avoid dangling pointers and double frees by setting pointers to NULL after freeing. Free nested allocations inner-first, give each block one owner, and use valgrind or ASan to catch leaks. You now have the full dynamic-array toolkit.

Frequently asked questions

Is the “Freeing and Avoiding Leaks” lesson free?

Yes — the full text of “Freeing and Avoiding Leaks” is free to read here on the web, and the C Academy course includes 4 lessons in total. To practise it interactively (a built-in code editor and a 24/7 AI tutor) and unlock the rest of the C Academy course, upgrade to CoddyKit PRO.

What will I learn in “Freeing and Avoiding Leaks”?

Clean up correctly. You practise C Academy with hands-on code you run directly in the browser, and a 24/7 AI tutor answers your questions as you work through the lesson.

Do I need any experience to start C Academy?

No prior experience is required. C Academy on CoddyKit is structured for beginners through advanced learners; this is — lesson 4 of 4, so you can start here or from the beginning and move at your own pace.

How long does the “Freeing and Avoiding Leaks” lesson take?

Most CoddyKit lessons take about 5–10 minutes. Each one is bite-sized and interactive, so you make steady progress and pick up exactly where you left off across the web and the app.

Can I write and run code in this C Academy lesson?

Yes. Every C Academy lesson includes a built-in code editor, so you write and run real code right in your browser and get instant AI feedback — no local setup required.

All lessons in this course

  1. Allocating an Array
  2. Growing with realloc
  3. A Reusable Vector Type
  4. Freeing and Avoiding Leaks
← Back to C Academy