Memory Layout — where things live - MarekBykowski/readme GitHub Wiki

High address
┌─────────────────┐
│   stack         │  local vars, function args, return addr — grows DOWN
│        ↓        │  fixed size (usually 8MB on Linux), overflow = segfault
├─────────────────┤
│   (gap)         │
│        ↑        │
│   heap          │  malloc/free — grows UP, you manage it manually
├─────────────────┤
│   BSS           │  uninitialised globals & statics → zero-initialised by OS
├─────────────────┤
│   data          │  initialised globals & statics (e.g. int x = 42;)
├─────────────────┤
│   text (code)   │  executable instructions — read-only
└─────────────────┘
Low address
int g_init   = 42;      // data segment
int g_uninit;           // BSS — zeroed automatically

void foo() {
    int local  = 1;     // stack
    int *p     = malloc(4); // p on stack, *p on heap
    static int s = 0;   // BSS (uninit static) or data (init static)
}

💡 In embedded (no OS): BSS is zeroed by startup code (crt0), heap may not exist at all — everything on stack or statically allocated.


🗑️ malloc / free — and what goes wrong

// Basic usage
int *p = malloc(sizeof(int) * 10);
if (!p) { /* always check! malloc returns NULL on failure */ }
memset(p, 0, sizeof(int) * 10);   // heap memory is NOT zeroed
free(p);
p = NULL;   // good habit — prevents use-after-free

// calloc — allocates AND zeroes
int *p2 = calloc(10, sizeof(int));

// realloc — resize existing allocation
p2 = realloc(p2, 20 * sizeof(int));  // may move the block!
Bug What happens How to detect
Memory leak malloc without free — heap grows until OOM Valgrind, AddressSanitizer
Double-free free(p); free(p); → UB, heap corruption ASan, guard pages
Use-after-free access *p after free(p) → UB, stale data or crash ASan
Buffer overflow write past end of malloc'd block → heap corruption ASan, bounds checking
NULL deref skipping the if (!p) check → crash code review
// Safe pattern
void *safe_malloc(size_t n) {
    void *p = malloc(n);
    if (!p) { perror("malloc"); exit(EXIT_FAILURE); }
    return p;
}

⚠️ In embedded: avoid dynamic allocation entirely if possible. Heap fragmentation on a microcontroller with 64KB RAM can cause allocation failures hours into a flight.