free and realloc
Giving memory back, and resizing it.
5 minute read
Big idea
Every block from malloc or calloc must be given back with free. realloc resizes a block — growing it when you need more room.
free
free(nums);
nums = NULL;
free(p)returns the block to the system. After this,pstill holds the old address, but that memory isn't yours any more.- Setting
p = NULLafterwards is a good habit: using it by mistake then fails loudly instead of silently corrupting memory. - Free exactly once. Freeing the same block twice (a double free) can crash the program.
Forgetting to free is a memory leak: the block stays reserved until the program exits. In a small program it's harmless; in a server that runs for weeks, leaks pile up until memory runs out.
realloc: grow as you go
int *bigger = realloc(nums, new_count * sizeof(int));
realloc keeps the old contents and gives you a block of the new size. It may move the data to a new address, so always use the pointer it returns. And if it fails it returns NULL — without freeing the old block — so assign to a temporary first:
#include <stdio.h>
#include <stdlib.h>
int main(void) {
int capacity = 2, count = 0;
int *list = malloc(capacity * sizeof(int));
if (list == NULL) return 1;
int value;
while (scanf("%d", &value) == 1) {
if (count == capacity) {
capacity *= 2;
int *grown = realloc(list, capacity * sizeof(int));
if (grown == NULL) {
free(list);
return 1;
}
list = grown;
printf("(grew to %d)\n", capacity);
}
list[count++] = value;
}
for (int i = 0; i < count; i++) printf("%d ", list[i]);
printf("\n");
free(list);
return 0;
}input
5 8 13 21 34 55output
(grew to 4)
(grew to 8)
5 8 13 21 34 55 Run and edit it in the app.
Doubling means only a few resizes even for thousands of values — this is how dynamic arrays in most languages work under the hood.
Watch it
Why the temporary?
list = realloc(list, 4 * sizeof(int)); // ✗ if this returns NULL, the only
// copy of the old address is gone
int *grown = realloc(list, 4 * sizeof(int)); // ✓ the old block is still in list
if (grown == NULL) {
free(list); // so it can be freed properly
return 1;
}
list = grown;
The first version works whenever realloc succeeds — which is why the bug survives testing. But on the day memory runs out, it leaks the original block and loses the data in it.
Why store realloc's result in a temporary pointer first?
The rules, in one place
- Every
malloc/calloc/realloc→ exactly onefree. - Check every allocation for
NULL. - Never use memory after freeing it; set the pointer to
NULL. reallocinto a temporary.
Think of it like
Borrowing chairs for a party. free returns them. realloc swaps your 4 chairs for 8 — maybe from a different stack, so check where they are now. Forget to return them and the rental shop runs out.
🕷️ Spidey-sense: Everything I web up, I clean up. Leave webs all over Queens and eventually there's no room to swing — that's a memory leak.
Key terms
free— Returns heap memory.realloc— Resizes a heap block, possibly moving it.- Memory leak — Memory that's never freed.
- Dangling pointer — A pointer to memory that has been freed.
- Double free — Freeing the same block twice.
Check yourself
What is a memory leak?
After free(p), p…
realloc may…