devnotes.

malloc, calloc, realloc, and free

C programs use two regions of memory: the stack, which automatically manages local variables and shrinks/grows with function calls, and the heap, which the programmer manages manually — this is what <stdlib.h>'s memory functions control.

#include <stdlib.h>

int *arr = malloc(5 * sizeof(int));  // allocate space for 5 ints, uninitialized
if (arr == NULL) {                    // ALWAYS check — allocation can fail
    fprintf(stderr, "Allocation failed\n");
    return 1;
}

int *zeroed = calloc(5, sizeof(int)); // like malloc, but zero-initializes the memory

arr = realloc(arr, 10 * sizeof(int)); // resize to hold 10 ints, keeping existing data
if (arr == NULL) { /* handle failure */ }

for (int i = 0; i < 10; i++) arr[i] = i * i;

free(arr);   // return the memory to the system
free(zeroed);
arr = NULL;  // good practice: avoid leaving a dangling reference

malloc(n) reserves n bytes on the heap and returns a pointer to the start, or NULL if the allocation fails (e.g., the system is out of memory) — checking for NULL before using the pointer is not optional in careful C code. calloc does the same but additionally zero-initializes every byte, useful when uninitialized garbage values would be dangerous. realloc resizes an existing allocation, possibly moving it to a new address if it can't be extended in place — which is exactly why you must always capture its return value (arr = realloc(arr, ...)), since the old pointer may become invalid.

Every successful malloc/calloc/realloc must eventually be matched with exactly one free() call — the heap doesn't clean itself up automatically the way the stack does. The next lesson covers what goes wrong when this rule is broken.

malloc, calloc, realloc, and free in C | DevNotes