Two places a C program keeps data
The stack and the heap are the two places where a C program keeps its variables. The stack holds local variables and cleans itself up, while the heap holds memory you ask for with malloc and must give back with free.
A till program that forgets to free a receipt on every sale runs out of memory by the end of the day.
By the end of this lesson
Note
I can say where a variable lives and what malloc and free do.
Key words
| Word | What it means | Example |
|---|---|---|
| Stack | memory for local variables, tidied up when a function returns | |
| Heap | memory a program asks for and gives back by hand | |
| malloc | a function that asks the heap for a number of bytes | malloc(40) |
| free | a function that gives heap memory back | free(p) |
| Memory leak | heap memory that is never given back | |
| Dangling pointer | a pointer to memory that has been freed |
The cells students fill in are left blank in this preview.
Local variables live on the stack
Each time a function is called, C reserves a block of the stack for it, called a stack frame. It holds the function's arguments and local variables.
When the function returns, its frame is thrown away and the variables are gone. The next call starts with a new frame, so locals do not remember anything.
Worked example: the stack while add runs
| Frame | Function | Holds |
|---|---|---|
| Top, newest | add | a = 4, b = 5 |
| Middle | total | The prices and a running sum |
| Bottom, oldest | main | The shopping basket |
| After add returns | add is gone | total is the top frame again |
The cells students fill in are left blank in this preview.
A local starts fresh on every call
int fresh(void) { int n = 0; n = n + 1; return n; }
// static keeps n out of the stack frame, so it survives between calls.
int kept(void) { static int n = 0; n = n + 1; return n; }
// fresh() gives 1 every time. kept() gives 1, then 2, then 3.
Write and build the two functions
printf '%s\n' '#include <stdio.h>' 'int fresh(void) { int n = 0; n = n + 1; return n; }' 'int kept(void) { static int n = 0; n = n + 1; return n; }' 'int main(void) {' ' int a = kept();' ' int b = kept();' ' int c = kept();' ' printf("fresh: %d\n", fresh());' ' printf("fresh: %d\n", fresh());' ' printf("kept: %d %d %d\n", a, b, c);' ' return 0;' '}' | gcc -Wall -x c - -o framesShown here, not run. Create a free account to press Run and type it into your own lab’s terminal.
Call each one and compare
./framesShown here, not run. Create a free account to press Run and type it into your own lab’s terminal.
malloc asks the heap for memory
malloc(n) asks the heap for n bytes and gives back a pointer to them, or NULL if there is no room. That memory stays yours until you call free on the same pointer.
Use the heap when memory must outlive the function that made it, or when its size is not known until the program runs.
Stack and heap side by side
| Stack | Heap | |
|---|---|---|
| Made by | A function call | malloc |
| Lasts until | The function returns | free is called |
| Tidied up by | The program itself | You |
| Mistakes cause | Crashes | Leaks and crashes |
The cells students fill in are left blank in this preview.
Write and build a malloc program
printf '%s\n' '#include <stdio.h>' '#include <stdlib.h>' 'int main(void) {' ' int *p = malloc(3 * sizeof(int));' ' if (p == NULL) return 1;' ' p[0] = 4; p[1] = 5; p[2] = 6;' ' printf("sum %d\n", p[0] + p[1] + p[2]);' ' free(p);' ' p = NULL;' ' printf("freed, pointer reset: %d\n", p == NULL);' ' return 0;' '}' | gcc -Wall -x c - -o heapShown here, not run. Create a free account to press Run and type it into your own lab’s terminal.
Use heap memory, then free it
./heapShown here, not run. Create a free account to press Run and type it into your own lab’s terminal.
Compare an array with a pointer
printf '%s\n' '#include <stdio.h>' '#include <stdlib.h>' 'int main(void) {' ' int local[10];' ' int *onheap = malloc(10 * sizeof(int));' ' printf("array %zu, pointer %zu\n", sizeof(local), sizeof(onheap));' ' free(onheap);' ' return 0;' '}' | gcc -Wall -x c - -o szShown here, not run. Create a free account to press Run and type it into your own lab’s terminal.
Print the two sizes
./szShown here, not run. Create a free account to press Run and type it into your own lab’s terminal.
Leaks and dangling pointers are heap bugs
A memory leak happens when the last pointer to a block is lost before free. The block can never be returned, so a long-running program slowly fills memory.
A dangling pointer happens when you use a pointer after free. The memory may now belong to something else, so reading or writing it gives wrong data or a crash.
One leak and one use after free
void leak(void) {
int *p = malloc(100);
p = NULL; // the only pointer is lost: 100 bytes leaked
}
void dangling(void) {
int *q = malloc(100);
free(q);
q[0] = 7; // use after free: undefined behaviour
}
A common mistake
Warning
Many people think free(p) erases the data and sets p to NULL. It only hands the bytes back. p still holds the old address, so set p = NULL yourself straight after free. Freeing the same pointer twice can crash the program.