free
Every block you get from malloc, calloc, or realloc is borrowed, not owned forever. free is how you give a block back to the allocator so the memory can be reused for the next request. Returning the storage-locker key, in the earlier picture. If you never return keys, the room of lockers fills up and no one else can get one — that is a memory leak.
Precisely: free(p) takes a pointer p that was returned by a previous allocation (or NULL, which it safely ignores), and marks that block as available again. After free returns, the block no longer belongs to you; the allocator may hand the same memory to a future malloc. Two things free does NOT do, which surprise beginners. First, free does not change the value of p — your variable still holds the old address, which now points into memory you no longer own. Such a pointer is a dangling pointer, and using it is undefined behavior; the safe habit is to write p = NULL right after freeing. Second, free does not erase the bytes — the old contents may linger, so a dangling read might appear to work by luck, which makes the bug intermittent and hard to find.
There is a strict contract. You must pass free exactly the pointer the allocator returned — not a pointer into the middle of the block, not a stack address, not a string literal. You must free each block exactly once: freeing the same block twice is a double-free, which corrupts the allocator's bookkeeping and is undefined behavior. Calling free(NULL) is explicitly allowed and does nothing, which is why you can safely free a pointer you have nulled out. Match every successful allocation with exactly one free, and most heap bugs disappear.
char *s = malloc(64); /* ... use s ... */ free(s); s = NULL; /* defuse the dangling pointer; free(NULL) is harmless later */
free returns the block; setting the pointer to NULL afterward prevents accidental use-after-free and double-free.
free does not zero memory and does not null your pointer. The pointer is still dangling after free until you set it to NULL yourself.