$ overview
overview/0x0B·Pointers & Memory

Dynamic Memory

Asking the heap for space with malloc, and giving it back with free.

Every array you've written so far had a size fixed at compile time. Often you don't know how much space you need until the program is running — the number of lines in a file, or how many items a user enters. For that, C lets you request memory from the heap directly, using functions from <stdlib.h>.

malloc and free

malloc.c
1#include <stdlib.h>
2 
3int *nums = malloc(5 * sizeof(int));
4 
5if (nums == NULL) {
6 // malloc failed — the system is out of memory
7 return 1;
8}
9 
10for (int i = 0; i < 5; i++) {
11 nums[i] = i * i;
12}
13 
14free(nums); // give the memory back when you're done
15nums = NULL; // avoid leaving a dangling pointer around

malloc takes a number of bytes and returns a pointer to that much freshly reserved memory — or NULL if the request couldn't be satisfied. Unlike a local array, memory from malloc survives after the function that allocated it returns, until you explicitly free it.

warning: Every malloc needs exactly one matching free
Forgetting to free memory you're done with is a memory leak — the program keeps holding memory it no longer needs. Calling free twice on the same pointer, or using a pointer after it's freed, is undefined behavior and a common source of crashes.

calloc: allocate and zero-initialize

calloc.c
1int *nums = calloc(5, sizeof(int));
2// equivalent to malloc(5 * sizeof(int)) but every byte starts at 0

realloc: change an allocation's size

realloc.c
1int *nums = malloc(5 * sizeof(int));
2// ... fill it, then decide you need more room ...
3 
4int *bigger = realloc(nums, 10 * sizeof(int));
5if (bigger == NULL) {
6 // realloc failed — the original 'nums' is still valid and unchanged
7 free(nums);
8 return 1;
9}
10nums = bigger;

realloc may move the block to a new address if it can't grow in place, copying the existing contents for you. This is why the result is always reassigned rather than assumed to be the same pointer.

Stack vs. heap

  • Stack — local variables and function parameters. Automatically created and destroyed as functions are called and return. Fast, but limited in size and scope.
  • Heap — memory you request explicitly with malloc. Lives until you free it, which means it can outlive the function that created it — and also means nothing cleans it up automatically.
error: Never return a pointer to a local variable
The address is technically returned, but the memory it points to no longer belongs to anything. Reading through it afterward is undefined behavior. If a function needs to hand back memory that outlives it, that memory must come from malloc.
dangling.c
1int *broken(void) {
2 int local = 42;
3 return &local; // local's stack memory is gone once this function returns
4}