SyntaxStudy
Sign Up
C Beginner 1 min read

Stack vs Heap Memory

Every C program uses two main memory regions: the stack and the heap. The stack is a region of memory managed automatically by the runtime. Each time a function is called, a stack frame is pushed; it holds the function's local variables, parameters, and return address. When the function returns, its stack frame is popped and that memory is reclaimed instantly. The stack is fast but limited in size (typically 1–8 MB) and its lifetime is tied to the enclosing scope. The heap is a large, general-purpose memory region that the program manages explicitly through `malloc`, `calloc`, `realloc`, and `free`. Heap allocations persist until explicitly freed — they are not scoped. This makes the heap suitable for data that needs to outlive the function that created it, or whose size is not known at compile time. The trade-off is responsibility: the programmer must free every allocation exactly once, at the right time. A common bug is returning a pointer to a local (stack) variable. Once the function returns, that stack frame is gone and the pointer dangles — it points to memory that may be overwritten at any time. Always allocate data on the heap when it needs to outlive its creating function, or use output parameters and let the caller provide the storage.
Example
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

/* BUG: returns pointer to local stack variable */
/* DO NOT do this in real code!                  */
int *bad_return(void)
{
    int local = 42;
    return &local;   /* dangling pointer — undefined behaviour */
}

/* CORRECT: allocate on heap so it outlives the function */
int *good_return(int value)
{
    int *p = malloc(sizeof(int));
    if (p) *p = value;
    return p;
}

/* CORRECT: caller provides storage */
void fill_values(int *out, int n)
{
    for (int i = 0; i < n; i++) out[i] = i * 10;
}

int main(void)
{
    /* Stack allocation — automatic lifetime */
    int  stack_arr[8];
    fill_values(stack_arr, 8);
    printf("Stack: %d %d %d\n", stack_arr[0], stack_arr[1], stack_arr[2]);

    /* Heap allocation — manual lifetime */
    int *heap_arr = malloc(8 * sizeof(int));
    if (!heap_arr) { perror("malloc"); return EXIT_FAILURE; }
    fill_values(heap_arr, 8);
    printf("Heap:  %d %d %d\n", heap_arr[0], heap_arr[1], heap_arr[2]);
    free(heap_arr);

    /* Heap scalar */
    int *hp = good_return(99);
    if (hp) {
        printf("Heap int: %d\n", *hp);
        free(hp);
    }

    printf("sizeof stack_arr: %zu bytes\n", sizeof(stack_arr));
    return 0;
}