C Fundamentals

Memory Management in C

Master the two memory worlds: Stack vs Heap, and learn to manage memory like a pro.

๐Ÿค” Why Do We Need to Understand Memory?

๐Ÿ’ก The "Your Room" Analogy

Imagine your program is like your bedroom:

  • You have a small desk (stack) where you keep things you need right now โ€” your notebook, pen, calculator. It's organized, easy to reach, but limited space.
  • You have a big closet (heap) where you store everything else โ€” clothes, old books, boxes. It's larger but you have to manually organize it.

In C, you get to choose where your data lives. Making the right choice keeps your program fast and efficient!

๐Ÿฝ๏ธ Stack vs Heap: The Real-World Analogy

๐Ÿ“š The "Stack of Plates" (Stack Memory)

        ๐Ÿฝ๏ธ STACK OF PLATES
        
        Think of a cafeteria plate dispenser:
        
                โ”Œโ”€โ”€โ”€โ”€โ”€โ”  โ† TOP (most recent)
                โ”‚ ๐Ÿฝ๏ธ โ”‚  Plate 3 (last function call)
                โ”œโ”€โ”€โ”€โ”€โ”€โ”ค
                โ”‚ ๐Ÿฝ๏ธ โ”‚  Plate 2 (middle function)
                โ”œโ”€โ”€โ”€โ”€โ”€โ”ค
                โ”‚ ๐Ÿฝ๏ธ โ”‚  Plate 1 (main function)
                โ”œโ”€โ”€โ”€โ”€โ”€โ”ค
                โ”‚     โ”‚
                โ””โ”€โ”€โ”€โ”€โ”€โ”˜  โ† BOTTOM
        
        ๐Ÿ”‘ KEY CHARACTERISTICS:
        โ€ข Last In, First Out (LIFO)
        โ€ข Automatic: Plates are pushed on and popped off
        โ€ข Limited: Only so many plates fit
        โ€ข Fast: Just grab from the top!
        
        In C: Local variables and function calls live here
        

๐Ÿงธ The "Heap of Toys" (Heap Memory)

        ๐Ÿงธ HEAP OF TOYS
        
        Think of a toy box where toys are scattered:
        
        โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
        โ”‚                                     โ”‚
        โ”‚    ๐Ÿš—        ๐Ÿค–                     โ”‚
        โ”‚                                     โ”‚
        โ”‚          ๐Ÿงฉ              ๐ŸŽฎ          โ”‚
        โ”‚                                     โ”‚
        โ”‚                ๐ŸŽฏ                    โ”‚
        โ”‚     ๐ŸŽฒ                              โ”‚
        โ”‚                                     โ”‚
        โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
        
        ๐Ÿ”‘ KEY CHARACTERISTICS:
        โ€ข No order: Toys are placed anywhere
        โ€ข Manual: YOU must pick up and put away
        โ€ข Large: Much more space than the plate stack
        โ€ข Slower: Have to search for space
        
        In C: Dynamic allocation with malloc/calloc lives here
        

๐Ÿ“Š Stack vs Heap Comparison

FeatureStack ๐Ÿฝ๏ธHeap ๐Ÿงธ
OrganizationNeat stack (LIFO)Scattered blocks
AllocationAutomaticManual (you control it)
DeallocationAutomatic when function endsManual (free())
SizeLimited (usually 1-8 MB)Larger (GB available)
SpeedVery fastSlower
ScopeLocal to functionGlobal access
FragmentationNeverCan happen

๐Ÿ’พ C Program Memory Layout

๐Ÿ—๏ธ How Your Program Uses Memory

โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚                                          โ”‚
โ”‚   HIGH MEMORY ADDRESS                    โ”‚
โ”‚   (0xFFFFFFFF on 32-bit)                 โ”‚
โ”‚                                          โ”‚
โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค โ† Stack starts here
โ”‚                                          โ”‚     โ†“ GROWS DOWN
โ”‚              STACK ๐Ÿฝ๏ธ                    โ”‚    โ†“     โ†“     โ†“
โ”‚     Local variables, function calls      โ”‚
โ”‚     Grows DOWNWARD (toward lower addresses)โ”‚
โ”‚                                          โ”‚
โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค
โ”‚                                          โ”‚
โ”‚              โ†•๏ธ GAP โ†•๏ธ                    โ”‚
โ”‚     Free space for heap to grow          โ”‚
โ”‚                                          โ”‚
โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค
โ”‚                                          โ”‚     โ†‘ GROWS UP
โ”‚               HEAP ๐Ÿงธ                    โ”‚    โ†‘     โ†‘     โ†‘
โ”‚     malloc(), calloc(), realloc()        โ”‚
โ”‚     Grows UPWARD (toward higher addresses)โ”‚
โ”‚                                          โ”‚
โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค โ† Heap starts here
โ”‚                                          โ”‚
โ”‚               BSS ๐Ÿ“‹                     โ”‚
โ”‚     Uninitialized global/static variablesโ”‚
โ”‚     (set to 0 by default)                โ”‚
โ”‚                                          โ”‚
โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค
โ”‚                                          โ”‚
โ”‚            DATA SEGMENT ๐Ÿ“Š               โ”‚
โ”‚     Initialized global/static variables  โ”‚
โ”‚                                          โ”‚
โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค
โ”‚                                          โ”‚
โ”‚           TEXT/CODE SEGMENT ๐Ÿ“–           โ”‚
โ”‚     Your program's instructions          โ”‚
โ”‚     (read-only, executable)              โ”‚
โ”‚                                          โ”‚
โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค
โ”‚                                          โ”‚
โ”‚    LOW MEMORY ADDRESS                    โ”‚
โ”‚    (0x00000000)                          โ”‚
โ”‚                                          โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

๐Ÿ’ก MEMORY MAP ANATOMY:
โ€ข Text/Code: Where your compiled code lives
โ€ข Data: Global variables with values you set
โ€ข BSS: Global variables you declared but didn't initialize
โ€ข Heap: Dynamic memory you request (malloc)
โ€ข Stack: Function calls and local variables
        

๐Ÿ“ Memory Regions Quick Reference

RegionWhat Lives HereWho Manages It
Text/CodeProgram instructions, compiled codeOS (read-only)
DataInitialized global/static variablesCompiler
BSSUninitialized global/static variablesOS (zeroed)
HeapDynamically allocated memoryYou! (malloc/free)
StackLocal variables, function parametersAutomatic

๐Ÿ’ป Stack vs Heap in Code

๐Ÿ“Š Visual: What Goes Where

Code Example:
โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚ int global = 10;         // DATA segment                   โ”‚
โ”‚ int uninit;              // BSS segment (starts as 0)    โ”‚
โ”‚                                                            โ”‚
โ”‚ void myFunction() {                                       โ”‚
โ”‚     int local = 5;       // STACK ๐Ÿฝ๏ธ                     โ”‚
โ”‚     int arr[10];          // STACK ๐Ÿฝ๏ธ                     โ”‚
โ”‚                                                            โ”‚
โ”‚     int *ptr = malloc(100);  // HEAP ๐Ÿงธ                   โ”‚
โ”‚     // ptr (the variable) is on STACK                      โ”‚
โ”‚     // but points to memory on HEAP                        โ”‚
โ”‚                                                            โ”‚
โ”‚     free(ptr);           // Give HEAP memory back         โ”‚
โ”‚ }                                                          โ”‚
โ”‚                                                            โ”‚
โ”‚ int main() {                                               โ”‚
โ”‚     myFunction();                                         โ”‚
โ”‚     return 0;                                             โ”‚
โ”‚ }                                                          โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

MEMORY DURING EXECUTION:
โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚ STACK (top)                              โ”‚
โ”‚ โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”   โ”‚
โ”‚ โ”‚ main()'s stack frame               โ”‚   โ”‚
โ”‚ โ”‚   (return address, etc.)           โ”‚   โ”‚
โ”‚ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜   โ”‚
โ”‚ โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”   โ”‚
โ”‚ โ”‚ myFunction()'s stack frame         โ”‚   โ”‚
โ”‚ โ”‚   local = 5                        โ”‚   โ”‚
โ”‚ โ”‚   arr[10] = {?,?,?,?,?,?,?,?,?,?}  โ”‚   โ”‚
โ”‚ โ”‚   ptr = 0x2000 (address on heap)   โ”‚   โ”‚
โ”‚ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜   โ”‚
โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค
โ”‚                                          โ”‚
โ”‚ HEAP:                                    โ”‚
โ”‚ โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”   โ”‚
โ”‚ โ”‚ Address 0x2000: 100 bytes allocated โ”‚   โ”‚
โ”‚ โ”‚ for ptr                            โ”‚   โ”‚
โ”‚ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜   โ”‚
โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค
โ”‚ DATA: global = 10                        โ”‚
โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค
โ”‚ BSS: uninit = 0                          โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
                        
stack_vs_heap.c
#include <stdio.h>
#include <stdlib.h>

void demonstrateStack() {
    // These live on the STACK
    int localVar = 10;        // Stack variable
    int localArray[100];      // Stack array
    
    printf("Stack variable: %d\n", localVar);
    printf("Stack array size: %zu bytes\n", sizeof(localArray));
    
    // All stack variables are automatically freed
    // when this function returns!
}

void demonstrateHeap() {
    // This lives on the HEAP
    int *heapArray = (int*)malloc(100 * sizeof(int));
    
    if (heapArray == NULL) {
        printf("Memory allocation failed!\n");
        return;
    }
    
    printf("Heap memory allocated!\n");
    
    // Use the memory
    for (int i = 0; i < 100; i++) {
        heapArray[i] = i * 10;
    }
    
    printf("First value: %d\n", heapArray[0]);
    printf("Last value: %d\n", heapArray[99]);
    
    // MUST manually free heap memory!
    free(heapArray);
    printf("Heap memory freed!\n");
}

int main() {
    printf("=== Stack Demo ===\n");
    demonstrateStack();
    
    printf("\n=== Heap Demo ===\n");
    demonstrateHeap();
    
    return 0;
}

๐ŸŽ Dynamic Memory Functions Explained Simply

๐Ÿ’ก The "Library Book" Analogy

Think of heap memory like borrowing books from a library:

๐Ÿ“š malloc() - "Asking for Memory"

Like asking the librarian for books

๐Ÿ“– MALLOC - Memory ALLOCation

You: "Can I have 100 bytes of memory?"
OS (Operating System): "Sure, here you go!"

โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚  int *ptr = malloc(100);              โ”‚
โ”‚                                         โ”‚
โ”‚  Before:                                โ”‚
โ”‚  ptr โ†’ ??? (uninitialized)              โ”‚
โ”‚                                         โ”‚
โ”‚  After:                                 โ”‚
โ”‚  ptr โ†’ [???][???][???]... (100 bytes)   โ”‚
โ”‚           โ†‘                             โ”‚
โ”‚        Memory contains GARBAGE values   โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

โš ๏ธ malloc gives you RAW memory - it could contain
   any random values! Always initialize before use.
                        

โœจ calloc() - "Asking for Clean Memory"

Like asking for new, clean books

โœจ CALLOC - Contiguous ALLOCation with Zero

You: "Can I have 10 integers, all set to 0?"
OS: "Sure, here are 10 clean, zeroed integers!"

โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚  int *ptr = calloc(10, sizeof(int));    โ”‚
โ”‚                                         โ”‚
โ”‚  Before:                                โ”‚
โ”‚  ptr โ†’ ???                              โ”‚
โ”‚                                         โ”‚
โ”‚  After:                                 โ”‚
โ”‚  ptr โ†’ [0][0][0][0][0][0][0][0][0][0]   โ”‚
โ”‚           โ†‘                             โ”‚
โ”‚        Memory is ZEROED/CLEAN!           โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

โœ… Use calloc when you need memory initialized to 0
   (e.g., for arrays, counters)
                        

๐Ÿ“ realloc() - "Resizing Your Memory"

Like asking for a bigger table at a restaurant

๐Ÿ“ REALLOC - REsize ALLOCated memory

Scenario: You asked for 5 integers, but now need 10!

โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚  int *ptr = malloc(5 * sizeof(int));              โ”‚
โ”‚  // ptr โ†’ [1][2][3][4][5]                          โ”‚
โ”‚                                                     โ”‚
โ”‚  // Oops, need more space!                          โ”‚
โ”‚  ptr = realloc(ptr, 10 * sizeof(int));            โ”‚
โ”‚                                                     โ”‚
โ”‚  After realloc:                                     โ”‚
โ”‚  ptr โ†’ [1][2][3][4][5][0][0][0][0][0]             โ”‚
โ”‚          โ†‘  Old data preserved โ†‘  New space        โ”‚
โ”‚                                                     โ”‚
โ”‚  โœ… Old values are kept!                            โ”‚
โ”‚  โœ… New space is available                          โ”‚
โ”‚  โš ๏ธ  May move to a new location if no room to grow  โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

๐Ÿ’ก realloc can also SHRINK memory if you need less space!
                        
malloc_calloc_realloc.c
#include <stdio.h>
#include <stdlib.h>

int main() {
    // ============ MALLOC ============
    printf("=== Using malloc ===\n");
    int *arr1 = (int*)malloc(5 * sizeof(int));
    
    if (arr1 == NULL) {
        printf("malloc failed!\n");
        return 1;
    }
    
    // malloc doesn't initialize - values are random!
    printf("After malloc:\n");
    for (int i = 0; i < 5; i++) {
        printf("arr1[%d] = %d\n", i, arr1[i]);  // Garbage values!
    }
    
    // Initialize manually
    for (int i = 0; i < 5; i++) {
        arr1[i] = (i + 1) * 10;  // 10, 20, 30, 40, 50
    }
    
    // ============ CALLOC ============
    printf("\n=== Using calloc ===\n");
    int *arr2 = (int*)calloc(5, sizeof(int));
    
    if (arr2 == NULL) {
        printf("calloc failed!\n");
        free(arr1);
        return 1;
    }
    
    // calloc initializes to 0!
    printf("After calloc:\n");
    for (int i = 0; i < 5; i++) {
        printf("arr2[%d] = %d\n", i, arr2[i]);  // All zeros!
    }
    
    // ============ REALLOC ============
    printf("\n=== Using realloc ===\n");
    printf("Before: arr1 has 5 elements\n");
    
    // Grow from 5 to 10 elements
    int *temp = realloc(arr1, 10 * sizeof(int));
    if (temp == NULL) {
        printf("realloc failed!\n");
        free(arr1);
        free(arr2);
        return 1;
    }
    arr1 = temp;
    
    printf("After realloc (10 elements):\n");
    printf("Old values preserved:\n");
    for (int i = 0; i < 5; i++) {
        printf("arr1[%d] = %d\n", i, arr1[i]);  // 10, 20, 30, 40, 50
    }
    
    // Clean up
    free(arr1);
    free(arr2);
    
    return 0;
}

๐Ÿ—‘๏ธ Why free() Matters

๐Ÿ“š The "Library Book" Analogy for Memory Leaks

MEMORY LEAK = BORROWING BOOKS BUT NEVER RETURNING

Scenario: You keep borrowing books but never return them...

Week 1:
โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚ LIBRARY SHELVES                         โ”‚
โ”‚ โ”Œโ”€โ”€โ”€โ”€โ”€โ”โ”Œโ”€โ”€โ”€โ”€โ”€โ”โ”Œโ”€โ”€โ”€โ”€โ”€โ”โ”Œโ”€โ”€โ”€โ”€โ”€โ”โ”Œโ”€โ”€โ”€โ”€โ”€โ”   โ”‚
โ”‚ โ”‚ ๐Ÿ“•  โ”‚โ”‚ ๐Ÿ“—  โ”‚โ”‚ ๐Ÿ“˜  โ”‚โ”‚ ๐Ÿ“™  โ”‚โ”‚ ๐Ÿ“•  โ”‚   โ”‚
โ”‚ โ””โ”€โ”€โ”€โ”€โ”€โ”˜โ””โ”€โ”€โ”€โ”€โ”€โ”˜โ””โ”€โ”€โ”€โ”€โ”€โ”˜โ””โ”€โ”€โ”€โ”€โ”€โ”˜โ””โ”€โ”€โ”€โ”€โ”€โ”˜   โ”‚
โ”‚                                         โ”‚
โ”‚ Your Books: [๐Ÿ“•][๐Ÿ“—] (borrowed)        โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

Week 5:
โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚ LIBRARY SHELVES                         โ”‚
โ”‚ โ”Œโ”€โ”€โ”€โ”€โ”€โ”โ”Œโ”€โ”€โ”€โ”€โ”€โ”โ”Œโ”€โ”€โ”€โ”€โ”€โ”โ”Œโ”€โ”€โ”€โ”€โ”€โ”โ”Œโ”€โ”€โ”€โ”€โ”€โ”   โ”‚
โ”‚ โ”‚ โŒ  โ”‚โ”‚ โŒ  โ”‚โ”‚ โŒ  โ”‚โ”‚ โŒ  โ”‚โ”‚ ๐Ÿ“™  โ”‚   โ”‚
โ”‚ โ””โ”€โ”€โ”€โ”€โ”€โ”˜โ””โ”€โ”€โ”€โ”€โ”€โ”˜โ””โ”€โ”€โ”€โ”€โ”€โ”˜โ””โ”€โ”€โ”€โ”€โ”€โ”˜โ””โ”€โ”€โ”€โ”€โ”€โ”˜   โ”‚
โ”‚                                         โ”‚
โ”‚ Your Books: [๐Ÿ“•][๐Ÿ“—][๐Ÿ“˜][๐Ÿ“™][๐Ÿ“•][๐Ÿ“—]   โ”‚
โ”‚              โ†‘ 10 books borrowed!       โ”‚
โ”‚              โ†‘ Never returned!           โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

Week 10:
โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚ LIBRARY SHELVES                         โ”‚
โ”‚ โ”Œโ”€โ”€โ”€โ”€โ”€โ”โ”Œโ”€โ”€โ”€โ”€โ”€โ”โ”Œโ”€โ”€โ”€โ”€โ”€โ”โ”Œโ”€โ”€โ”€โ”€โ”€โ”โ”Œโ”€โ”€โ”€โ”€โ”€โ”   โ”‚
โ”‚ โ”‚ โŒ  โ”‚โ”‚ โŒ  โ”‚โ”‚ โŒ  โ”‚โ”‚ โŒ  โ”‚โ”‚ โŒ  โ”‚   โ”‚
โ”‚ โ””โ”€โ”€โ”€โ”€โ”€โ”˜โ””โ”€โ”€โ”€โ”€โ”€โ”˜โ””โ”€โ”€โ”€โ”€โ”€โ”˜โ””โ”€โ”€โ”€โ”€โ”€โ”˜โ””โ”€โ”€โ”€โ”€โ”€โ”˜   โ”‚
โ”‚                                         โ”‚
โ”‚   NO MORE BOOKS AVAILABLE!              โ”‚
โ”‚   Library is "OUT OF MEMORY"            โ”‚
โ”‚                                         โ”‚
โ”‚   New borrowers can't get books!        โ”‚
โ”‚   Program crashes or becomes slow!      โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

IN C CODE:
โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚ void leakyFunction() {                  โ”‚
โ”‚     int *arr = malloc(1000);            โ”‚
โ”‚     // Process data...                  โ”‚
โ”‚     // FORGOT TO CALL FREE!             โ”‚
โ”‚ }  // Memory is lost forever!            โ”‚
โ”‚                                         โ”‚
โ”‚ Every call leaks 1000 bytes!           โ”‚
โ”‚ Call it 1000 times โ†’ 1MB lost!          โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
                        

๐ŸŽจ Visual: Memory Getting Filled Up

MEMORY LEAK VISUALIZATION

HEAP at start:                        HEAP after leak:
โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”              โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚                     โ”‚              โ”‚ โ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆ    โ”‚
โ”‚                     โ”‚              โ”‚ โ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆ    โ”‚
โ”‚                     โ”‚              โ”‚ โ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆ    โ”‚
โ”‚                     โ”‚              โ”‚ โ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆ    โ”‚
โ”‚                     โ”‚              โ”‚ โ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆ    โ”‚
โ”‚                     โ”‚              โ”‚ โ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆ    โ”‚
โ”‚                     โ”‚              โ”‚ โ–ˆโ–ˆโ–ˆ Leaked Memory   โ”‚
โ”‚                     โ”‚              โ”‚ โ–ˆโ–ˆโ–ˆ (can't use)     โ”‚
โ”‚                     โ”‚              โ”‚ โ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆ    โ”‚
โ”‚                     โ”‚              โ”‚                     โ”‚
โ”‚                     โ”‚              โ”‚ Available space โ†“   โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜              โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

Legend:
  โ–‘ = Free memory
  โ–ˆ = Used/leaked memory
  
The more you leak, the less memory is available!
                        

โš ๏ธ Common Memory Mistakes

Mistake #1: Using Memory After Free

โŒ WRONG - Use After Free
โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚ int *ptr = malloc(sizeof(int));         โ”‚
โ”‚ *ptr = 10;                              โ”‚
โ”‚ free(ptr);                              โ”‚
โ”‚                                         โ”‚
โ”‚ printf("%d\n", *ptr);  // DANGER!        โ”‚
โ”‚                         โ†‘               โ”‚
โ”‚              Using freed memory!        โ”‚
โ”‚              Could be anything now      โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

โœ… RIGHT - Set to NULL after free
โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚ int *ptr = malloc(sizeof(int));         โ”‚
โ”‚ *ptr = 10;                              โ”‚
โ”‚ free(ptr);                              โ”‚
โ”‚ ptr = NULL;  // Now it's safe!          โ”‚
โ”‚                                         โ”‚
โ”‚ if (ptr != NULL) {                      โ”‚
โ”‚     printf("%d\n", *ptr);                โ”‚
โ”‚ }                                       โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
                        

Mistake #2: Double Free

โŒ WRONG - Freeing Twice
โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚ int *ptr = malloc(sizeof(int));         โ”‚
โ”‚ *ptr = 10;                              โ”‚
โ”‚ free(ptr);                              โ”‚
โ”‚ free(ptr);   // CRASH!                  โ”‚
โ”‚              โ†‘                          โ”‚
โ”‚   Already freed, can't free again!      โ”‚
โ”‚   โ†’ Undefined behavior / crash           โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

โœ… RIGHT - Free once, set to NULL
โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚ int *ptr = malloc(sizeof(int));         โ”‚
โ”‚ *ptr = 10;                              โ”‚
โ”‚ free(ptr);                              โ”‚
โ”‚ ptr = NULL;  // Prevents double free    โ”‚
โ”‚                                         โ”‚
โ”‚ // Safe to call free(NULL) - does nothingโ”‚
โ”‚ free(ptr);  // No problem!              โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
                        

Mistake #3: Memory Leak Example

โŒ WRONG - Memory Leak in a Loop
โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚ void processData() {                    โ”‚
โ”‚     for (int i = 0; i < 1000; i++) {    โ”‚
โ”‚         int *temp = malloc(1000);       โ”‚
โ”‚         // Use temp...                   โ”‚
โ”‚         // FORGOT TO FREE!             โ”‚
โ”‚     }  // Leaks 1000 bytes each time    โ”‚
โ”‚ }  // Total leak: 1,000,000 bytes!      โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

โœ… RIGHT - Always Free What You Malloc
โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚ void processData() {                    โ”‚
โ”‚     for (int i = 0; i < 1000; i++) {    โ”‚
โ”‚         int *temp = malloc(1000);       โ”‚
โ”‚         // Use temp...                   โ”‚
โ”‚         free(temp);  // Clean up!       โ”‚
โ”‚     }  // No leak!                      โ”‚
โ”‚ }                                        โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
                        

Mistake #4: Not Checking malloc Return

โŒ WRONG - No NULL Check
โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚ int *arr = malloc(1000000000);          โ”‚
โ”‚                          โ†‘              โ”‚
โ”‚              What if this fails?        โ”‚
โ”‚ arr[0] = 10;  // CRASH! NULL pointer     โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

โœ… RIGHT - Always Check Return Value
โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚ int *arr = malloc(1000000000);          โ”‚
โ”‚ if (arr == NULL) {                       โ”‚
โ”‚     printf("Out of memory!\n");          โ”‚
โ”‚     return 1;  // Handle error gracefully โ”‚
โ”‚ }                                        โ”‚
โ”‚ arr[0] = 10;  // Safe!                  โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
                        
common_mistakes.c
#include <stdio.h>
#include <stdlib.h>

void safeMemoryUsage() {
    // GOOD: Check malloc return
    int *ptr = (int*)malloc(sizeof(int) * 100);
    if (ptr == NULL) {
        fprintf(stderr, "Memory allocation failed\n");
        return;
    }
    
    // Use the memory
    for (int i = 0; i < 100; i++) {
        ptr[i] = i * i;
    }
    
    // GOOD: Free once
    free(ptr);
    
    // GOOD: Set to NULL after free
    ptr = NULL;
    
    // Now safe - free(NULL) does nothing
    free(ptr);  // No crash!
}

void leakyFunction() {
    // BAD: Memory leak
    int *arr = (int*)malloc(1000);
    // Process data...
    // Oops! Forgot to free!
    // Memory is lost forever
}

void useAfterFreeExample() {
    int *ptr = (int*)malloc(sizeof(int));
    *ptr = 42;
    
    free(ptr);
    // ptr still points to old address, but it's invalid!
    
    // DANGER: Using freed memory!
    // printf("%d\n", *ptr);  // DON'T DO THIS!
    
    // GOOD: Set to NULL
    ptr = NULL;
}

int main() {
    safeMemoryUsage();
    
    // This would leak memory:
    // leakyFunction();
    
    return 0;
}

โœ… Memory Management Best Practices

๐Ÿ“‹ The Golden Rules of Memory

RuleWhy It MattersExample
1. Always check if malloc succeeded Prevents crashes when out of memory if (ptr == NULL) { handle_error(); }
2. Always free what you malloc Prevents memory leaks free(ptr);
3. Set pointer to NULL after free Prevents use-after-free bugs ptr = NULL;
4. Don't free twice Prevents crashes from double-free Setting NULL after free helps
5. Free in the same scope when possible Makes cleanup easier to track Allocate and free in same function
6. Use calloc when you need zeros Saves initialization code calloc(n, sizeof(int))

๐ŸŽฏ The Complete Pattern

โœ… THE COMPLETE SAFE PATTERN

โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚ 1. ALLOCATE with NULL initialization              โ”‚
โ”‚    int *ptr = NULL;                                โ”‚
โ”‚                                                     โ”‚
โ”‚ 2. REQUEST memory from OS                           โ”‚
โ”‚    ptr = malloc(size);                             โ”‚
โ”‚                                                     โ”‚
โ”‚ 3. CHECK if allocation succeeded                   โ”‚
โ”‚    if (ptr == NULL) {                              โ”‚
โ”‚        // Handle error                             โ”‚
โ”‚        return ERROR;                               โ”‚
โ”‚    }                                               โ”‚
โ”‚                                                     โ”‚
โ”‚ 4. USE the memory                                  โ”‚
โ”‚    ptr[0] = 10;                                    โ”‚
โ”‚                                                     โ”‚
โ”‚ 5. FREE when done                                  โ”‚
โ”‚    free(ptr);                                      โ”‚
โ”‚                                                     โ”‚
โ”‚ 6. SET to NULL to prevent use-after-free          โ”‚
โ”‚    ptr = NULL;                                     โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

MEMORY SAFETY CHECKLIST:
โ–ก Did I check malloc/calloc/realloc return value?
โ–ก Do I have a free() for every malloc()?
โ–ก Did I set the pointer to NULL after free?
โ–ก Is there any path where I return without freeing?
โ–ก Am I accessing memory after it was freed?
                        
best_practices_complete.c
#include <stdio.h>
#include <stdlib.h>

// Example of complete safe memory management
int* createArray(int size) {
    // Step 1: Declare with NULL
    int *arr = NULL;
    
    // Step 2: Allocate
    arr = (int*)malloc(size * sizeof(int));
    
    // Step 3: Check
    if (arr == NULL) {
        fprintf(stderr, "Failed to allocate %d integers\n", size);
        return NULL;
    }
    
    // Step 4: Use
    for (int i = 0; i < size; i++) {
        arr[i] = i + 1;
    }
    
    return arr;  // Caller must free!
}

// Safe function that allocates and frees
void processArray() {
    int *arr = createArray(10);
    if (arr == NULL) {
        return;  // Allocation failed
    }
    
    // Use the array
    printf("Array: ");
    for (int i = 0; i < 10; i++) {
        printf("%d ", arr[i]);
    }
    printf("\n");
    
    // Step 5: Free
    free(arr);
    
    // Step 6: Set to NULL
    arr = NULL;
}

int main() {
    printf("=== Safe Memory Example ===\n");
    processArray();
    
    printf("\nMemory handled safely!\n");
    return 0;
}

๐Ÿ“ Quick Reference Summary

๐Ÿ”‘ Key Takeaways

MEMORY TYPES CHEAT SHEET

โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚ STACK MEMORY (Automatic)                                โ”‚
โ”‚ โ€ข Local variables: int x = 5;                          โ”‚
โ”‚ โ€ข Function parameters                                   โ”‚
โ”‚ โ€ข Arrays: int arr[100];                                 โ”‚
โ”‚ โ€ข Allocated automatically when function starts          โ”‚
โ”‚ โ€ข Freed automatically when function ends                โ”‚
โ”‚ โ€ข Limited size (1-8 MB typically)                       โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚ HEAP MEMORY (Dynamic)                                   โ”‚
โ”‚ โ€ข malloc(size) - raw memory, uninitialized              โ”‚
โ”‚ โ€ข calloc(n, size) - zero-initialized memory             โ”‚
โ”‚ โ€ข realloc(ptr, new_size) - resize existing memory       โ”‚
โ”‚ โ€ข free(ptr) - return memory to OS                       โ”‚
โ”‚ โ€ข YOU must manage it!                                   โ”‚
โ”‚ โ€ข Large size available (GB)                               โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

THE GOLDEN RULE:
"For every malloc/calloc/realloc, there must be a free()"

COMMON PATTERN:
int *ptr = malloc(size);
if (ptr == NULL) { /* handle error */ }
// ... use memory ...
free(ptr);
ptr = NULL;