Dynamic Memory
The heapLocal variables live on the stack and disappear when their function returns. When you need memory whose size is only known at run time, or that must outlive a function, you ask the heap with malloc — and give it back with free. In the lab below you allocate, grow and free blocks, and watch the three classic heap bugs happen.
- mallocmemory from the heap
- freegives it back — exactly once
- NULLmalloc failed / pointer not in use
Ready
Keyboard: ← → step · Space play / pause · R reset · Esc exit classroom mode
The ideas behind the lab
Exactly what you watched happen, in plain steps.
Stack vs heap
The stack holds local variables: fast, automatic, but limited in size and gone when the function returns. The heap is a big pool you manage yourself: malloc(bytes) reserves a block and returns its address (or NULL if it fails); the block stays until you call free on it. Use the heap when the size is only known at run time, when data is large, or when it must outlive the function that created it.
#include <stdlib.h>
int n = 10;
int *a = malloc(n * sizeof *a); // n ints, garbage values
if (a == NULL) { /* out of memory */ }
a[0] = 5; // use it like an array
free(a); // give it back — once
a = NULL; // no dangling pointer
| Call | What it does | Contents | Returns |
|---|---|---|---|
| malloc(bytes) | Reserves a block | garbage | address or NULL |
| calloc(count, size) | Reserves count × size bytes | all zero | address or NULL |
| realloc(p, bytes) | Resizes a block (may move it) | old values kept | new address or NULL |
| free(p) | Gives the block back | — | nothing; free(NULL) does nothing |
Allocating
- malloc takes a size in bytes: n * sizeof *a works for any type.
- It returns void *, which C converts to your pointer type automatically.
- Always check for NULL before using the memory.
- calloc(n, size) is like malloc but fills the block with zeros.
- The block is used like an array: a[i] or *(a + i).
Resizing and freeing
- realloc(p, newSize) keeps the old contents and may move the block.
- Store its result in a temporary pointer: if it fails, p is still valid.
- free(p) returns a block; every malloc / calloc needs exactly one free.
- After free, set the pointer to NULL so it cannot be used by mistake.
- A function may return heap memory — the caller must then free it.
The classic bugs
- Memory leak: the last pointer to a block is lost before free.
- Dangling pointer / use after free: using memory after free.
- Double free: calling free twice on the same block — the program aborts.
- Freeing memory that was not from malloc (a local array) — undefined behaviour.
- Tools like Valgrind or -fsanitize=address find these bugs.
A function that builds an array on the heap
Every line, explained.
#include <stdio.h>
#include <stdlib.h>
malloc and free live here.
int *makeSquares(int n)
{
int *a = malloc(n * sizeof *a);
n ints on the heap.
if (a == NULL)
return NULL;
Pass the failure on to the caller.
for (int i = 0; i < n; i++)
a[i] = (i + 1) * (i + 1);
return a;
Fine: heap memory outlives the function (a local array would not).
}
int main(void)
{
int n = 5;
int *sq = makeSquares(n);
if (sq == NULL) {
printf("out of memory\n");
return 1;
}
for (int i = 0; i < n; i++)
printf("%d ", sq[i]);
Prints 1 4 9 16 25
printf("\n");
free(sq);
The caller frees what it received.
sq = NULL;
return 0;
}
Spot the bug
Mistakes almost every beginner makes — and how to fix them.
Forgetting sizeof
int *a = malloc(10); // 10 BYTES = 2 ints
int *a = malloc(10 * sizeof *a); // 10 ints
malloc counts bytes. Multiply by the size of one element.
Not checking for NULL
int *a = malloc(n * sizeof *a); a[0] = 1; // crash if malloc failed
int *a = malloc(n * sizeof *a); if (a == NULL) return 1; a[0] = 1;
When memory runs out, malloc returns NULL — and writing to NULL crashes.
Losing the block (leak)
p = malloc(100); p = malloc(200); // the first 100 bytes are lost
p = malloc(100); free(p); p = malloc(200);
Once no pointer holds a block's address, it can never be freed.
a = realloc(a, …)
a = realloc(a, 2 * n * sizeof *a); // NULL → old block lost
int *t = realloc(a, 2 * n * sizeof *a); if (t != NULL) a = t;
If realloc fails it returns NULL but keeps the old block — which you just overwrote.
Using memory after free
free(p);
printf("%d", *p);
printf("%d", *p);
free(p);
p = NULL;
After free the block may be reused for something else: reading it is undefined behaviour.
Check your understanding
Answer each question — you will see the explanation straight away.
Which call reserves room for 8 ints?
malloc takes a number of bytes.
What does malloc return when no memory is available?
Always compare the result with NULL.
What is the difference between malloc and calloc?
malloc leaves garbage; calloc zeroes the block.
How many times must a block from malloc be freed?
Zero times is a leak; twice is a double free.
Why is it safe for a function to return a pointer from malloc but not &local?
Heap blocks are not tied to any function call.
Dynamic Memory quiz
Test yourself: 10 questions, 70% to pass. Create a free account to take the quiz, save your progress and work towards the certificate.