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
1#include <stdlib.h>23int *nums = malloc(5 * sizeof(int));45if (nums == NULL) {6// malloc failed — the system is out of memory7return 1;8}910for (int i = 0; i < 5; i++) {11nums[i] = i * i;12}1314free(nums); // give the memory back when you're done15nums = 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.
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
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
1int *nums = malloc(5 * sizeof(int));2// ... fill it, then decide you need more room ...34int *bigger = realloc(nums, 10 * sizeof(int));5if (bigger == NULL) {6// realloc failed — the original 'nums' is still valid and unchanged7free(nums);8return 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 youfreeit, which means it can outlive the function that created it — and also means nothing cleans it up automatically.
malloc.1int *broken(void) {2int local = 42;3return &local; // local's stack memory is gone once this function returns4}