CStack vs Heap

Stack vs Heap

C gives you two very different places to store data while your program runs: the stack and the heap. Nearly every memory-related bug in C — leaks, dangling pointers, stack overflows, use-after-free — comes down to confusing the rules of one for the rules of the other. Picking the right one for a given piece of data is one of the most important design decisions you make when writing C.
Side-by-Side Comparison

Aspect

Stack

Heap

Allocation speed

Extremely fast -- just moves a pointer

Slower -- involves an allocator searching for free space

Size limits

Small and fixed per thread (often a few MB)

Large -- limited mainly by available system memory

Lifetime

Tied to the enclosing function/block; ends automatically on return

Lasts until you explicitly call free() (or the program exits)

Who manages it

The compiler, automatically

You, the programmer, manually

Management style

Automatic (no code needed)

Manual (malloc/calloc/realloc/free)

Typical contents

Local variables, function parameters

Data whose size or lifetime is not known until runtime

Risk if misused

Stack overflow (too much data or too-deep recursion)

Leaks, dangling pointers, double frees

The Stack: Fast, Automatic, Limited
Every time a function is called, the runtime pushes a new stack frame holding its parameters and local variables. When the function returns, that entire frame is popped off in one step — there is no bookkeeping, no risk of forgetting to clean up, and it is essentially free in terms of CPU cost.

C
#include <stdio.h>

int square(int n) {
    int result = n * n; // 'result' and 'n' live on the stack
    return result;
} // stack frame for square() is popped automatically here

int main(void) {
    int value = square(5); // 'value' also lives on the stack
    printf("%d\n", value);
    return 0;
} // main()'s stack frame is popped here

The tradeoff is that stack space is limited and its lifetime is rigid: a stack variable disappears the instant its function returns, and you cannot resize it or make it outlive its scope. Allocating a very large array on the stack, or recursing too deeply, can exhaust the stack entirely and crash the program.

C
void risky(void) {
    int huge[10000000]; // ~40MB on the stack -- likely to overflow
    huge[0] = 1;
}
The Heap: Flexible, Manual, Persistent
The heap is a much larger pool of memory that you request explicitly at runtime with malloc(), calloc(), or realloc(). Unlike stack memory, heap memory does not disappear when the function that allocated it returns — it persists until you explicitly release it with free(). That makes the heap the right place for data whose size is only known at runtime, or that needs to outlive the function that created it.

C
#include <stdio.h>
#include <stdlib.h>

int *make_array(int n) {
    int *arr = malloc(n * sizeof(int)); // lives on the HEAP
    if (arr == NULL) {
        return NULL;
    }
    for (int i = 0; i < n; i++) {
        arr[i] = i * i;
    }
    return arr; // safe to return -- this memory outlives make_array()
}

int main(void) {
    int *nums = make_array(5);
    if (nums == NULL) {
        return 1;
    }
    for (int i = 0; i < 5; i++) {
        printf("%d\n", nums[i]);
    }
    free(nums); // your responsibility -- the heap never cleans up for you
    return 0;
}
Returning a pointer to a local variable is a classic bug
Never return a pointer to a stack-allocated local variable from a function — the moment the function returns, that stack frame is gone, and the pointer becomes dangling. If data needs to outlive the function that creates it, allocate it on the heap instead.
Choosing Between Them
  • Use the stack for small, short-lived data whose size is known at compile time -- it is faster and requires no cleanup code

  • Use the heap when the size is only known at runtime, when the data is large, or when it must outlive the function that created it

  • Use the heap when you need to explicitly control when memory is released, independent of function call boundaries

  • Prefer the stack by default -- reach for the heap only when the stack genuinely cannot do the job

A simple mental model
Think of the stack as a stack of plates at a buffet: you can only add or remove from the top, and it happens automatically as functions are called and return. Think of the heap as a warehouse: you can request a box of any size, put it anywhere, keep it as long as you like — but nobody will clean it up for you until you carry it back out yourself.