c

examples

examples.cšŸ”§
/**
 * Dynamic Memory Allocation - Examples
 * 
 * This file demonstrates malloc, calloc, realloc, and free functions
 * for dynamic memory management in C.
 * 
 * Compile: gcc examples.c -o examples
 * Run: ./examples
 */

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

/* ============================================================
 * EXAMPLE 1: malloc() - Basic Memory Allocation
 * ============================================================ */

void example1_malloc_basics(void) {
    printf("=== EXAMPLE 1: malloc() Basics ===\n\n");
    
    // Allocate memory for a single integer
    int *ptr = (int *)malloc(sizeof(int));
    
    if (ptr == NULL) {
        printf("Memory allocation failed!\n");
        return;
    }
    
    printf("Single integer allocation:\n");
    printf("  Address allocated: %p\n", (void *)ptr);
    printf("  Size allocated: %zu bytes\n\n", sizeof(int));
    
    // Store a value
    *ptr = 42;
    printf("  Value stored: %d\n", *ptr);
    
    // Always free allocated memory
    free(ptr);
    printf("  Memory freed successfully.\n\n");
    
    // Allocate memory for an array of 5 integers
    int *arr = (int *)malloc(5 * sizeof(int));
    
    if (arr == NULL) {
        printf("Memory allocation failed!\n");
        return;
    }
    
    printf("Array of 5 integers:\n");
    printf("  Total size: %zu bytes\n", 5 * sizeof(int));
    
    // Initialize and display values
    for (int i = 0; i < 5; i++) {
        arr[i] = (i + 1) * 10;  // 10, 20, 30, 40, 50
    }
    
    printf("  Values: ");
    for (int i = 0; i < 5; i++) {
        printf("%d ", arr[i]);
    }
    printf("\n");
    
    free(arr);
    printf("  Memory freed successfully.\n");
}


/* ============================================================
 * EXAMPLE 2: malloc() - Memory Contains Garbage Values
 * ============================================================ */

void example2_malloc_garbage(void) {
    printf("\n=== EXAMPLE 2: malloc() Contains Garbage ===\n\n");
    
    int *arr = (int *)malloc(5 * sizeof(int));
    
    if (arr == NULL) {
        printf("Memory allocation failed!\n");
        return;
    }
    
    printf("Memory allocated but NOT initialized:\n");
    printf("Values (garbage): ");
    for (int i = 0; i < 5; i++) {
        printf("%d ", arr[i]);  // Unpredictable values!
    }
    printf("\n\n");
    
    printf("WARNING: Always initialize malloc'd memory before use!\n");
    
    // Proper initialization
    for (int i = 0; i < 5; i++) {
        arr[i] = 0;
    }
    
    printf("After initialization: ");
    for (int i = 0; i < 5; i++) {
        printf("%d ", arr[i]);
    }
    printf("\n");
    
    free(arr);
}


/* ============================================================
 * EXAMPLE 3: calloc() - Zero-Initialized Memory
 * ============================================================ */

void example3_calloc(void) {
    printf("\n=== EXAMPLE 3: calloc() - Zero-Initialized ===\n\n");
    
    // calloc(count, size) - allocates and initializes to zero
    int *arr = (int *)calloc(5, sizeof(int));
    
    if (arr == NULL) {
        printf("Memory allocation failed!\n");
        return;
    }
    
    printf("Syntax: calloc(number_of_elements, size_of_each)\n\n");
    printf("Allocated 5 integers with calloc:\n");
    printf("Values (auto-initialized to 0): ");
    for (int i = 0; i < 5; i++) {
        printf("%d ", arr[i]);  // All zeros!
    }
    printf("\n\n");
    
    // Modify values
    for (int i = 0; i < 5; i++) {
        arr[i] = i * i;  // 0, 1, 4, 9, 16
    }
    
    printf("After modification: ");
    for (int i = 0; i < 5; i++) {
        printf("%d ", arr[i]);
    }
    printf("\n");
    
    free(arr);
}


/* ============================================================
 * EXAMPLE 4: malloc() vs calloc() Comparison
 * ============================================================ */

void example4_malloc_vs_calloc(void) {
    printf("\n=== EXAMPLE 4: malloc() vs calloc() ===\n\n");
    
    printf("ā”Œā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”¬ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”¬ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”\n");
    printf("│  Feature   │           malloc()              │           calloc()              │\n");
    printf("ā”œā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”¼ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”¼ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”¤\n");
    printf("│ Syntax     │ malloc(size_in_bytes)           │ calloc(count, size_of_each)     │\n");
    printf("│ Arguments  │ 1 argument                      │ 2 arguments                     │\n");
    printf("│ Initialize │ NO (garbage values)             │ YES (zeros)                     │\n");
    printf("│ Speed      │ Slightly faster                 │ Slightly slower                 │\n");
    printf("ā””ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”“ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”“ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”˜\n\n");
    
    // Practical demonstration
    printf("Practical demonstration:\n\n");
    
    // malloc way
    int *m = (int *)malloc(3 * sizeof(int));
    if (m) {
        printf("malloc(3 * sizeof(int)): ");
        printf("[%d, %d, %d] (garbage)\n", m[0], m[1], m[2]);
        free(m);
    }
    
    // calloc way
    int *c = (int *)calloc(3, sizeof(int));
    if (c) {
        printf("calloc(3, sizeof(int)): ");
        printf("[%d, %d, %d] (zeros)\n", c[0], c[1], c[2]);
        free(c);
    }
}


/* ============================================================
 * EXAMPLE 5: realloc() - Resize Allocated Memory
 * ============================================================ */

void example5_realloc(void) {
    printf("\n=== EXAMPLE 5: realloc() - Resize Memory ===\n\n");
    
    // Start with 3 integers
    int *arr = (int *)malloc(3 * sizeof(int));
    if (arr == NULL) return;
    
    arr[0] = 10;
    arr[1] = 20;
    arr[2] = 30;
    
    printf("Initial allocation (3 integers):\n");
    printf("  Address: %p\n", (void *)arr);
    printf("  Values: [%d, %d, %d]\n\n", arr[0], arr[1], arr[2]);
    
    // Expand to 5 integers
    int *new_arr = (int *)realloc(arr, 5 * sizeof(int));
    
    if (new_arr == NULL) {
        printf("Reallocation failed! Original memory intact.\n");
        free(arr);
        return;
    }
    
    arr = new_arr;  // Update pointer
    
    printf("After realloc to 5 integers:\n");
    printf("  Address: %p %s\n", (void *)arr, 
           (arr == new_arr) ? "(might be same or different)" : "");
    printf("  Old values preserved: [%d, %d, %d, ?, ?]\n", 
           arr[0], arr[1], arr[2]);
    
    // Initialize new elements
    arr[3] = 40;
    arr[4] = 50;
    
    printf("  After initialization: [%d, %d, %d, %d, %d]\n\n", 
           arr[0], arr[1], arr[2], arr[3], arr[4]);
    
    // Shrink to 2 integers
    int *smaller = (int *)realloc(arr, 2 * sizeof(int));
    if (smaller) {
        arr = smaller;
        printf("After shrinking to 2 integers:\n");
        printf("  Values: [%d, %d]\n", arr[0], arr[1]);
    }
    
    free(arr);
    printf("\nMemory freed.\n");
}


/* ============================================================
 * EXAMPLE 6: realloc() Special Cases
 * ============================================================ */

void example6_realloc_special(void) {
    printf("\n=== EXAMPLE 6: realloc() Special Cases ===\n\n");
    
    // Case 1: realloc(NULL, size) == malloc(size)
    printf("Case 1: realloc(NULL, size) acts like malloc(size)\n");
    int *ptr1 = (int *)realloc(NULL, 3 * sizeof(int));
    if (ptr1) {
        ptr1[0] = 100;
        printf("  Allocated and assigned: ptr1[0] = %d\n", ptr1[0]);
        free(ptr1);
    }
    
    // Case 2: realloc(ptr, 0) - may free memory (implementation defined)
    printf("\nCase 2: realloc(ptr, 0)\n");
    printf("  Behavior is implementation-defined in C11+\n");
    printf("  Don't rely on this - use free() explicitly!\n");
    
    // Case 3: realloc can move memory
    printf("\nCase 3: realloc may move memory to a new location\n");
    int *original = (int *)malloc(2 * sizeof(int));
    if (original) {
        original[0] = 1;
        original[1] = 2;
        printf("  Original address: %p\n", (void *)original);
        
        // Request much larger size - likely to move
        int *resized = (int *)realloc(original, 1000 * sizeof(int));
        if (resized) {
            printf("  New address:      %p\n", (void *)resized);
            printf("  Data preserved: [%d, %d]\n", resized[0], resized[1]);
            // Don't use 'original' anymore - it's invalid!
            free(resized);
        }
    }
}


/* ============================================================
 * EXAMPLE 7: free() and Dangling Pointers
 * ============================================================ */

void example7_free_dangling(void) {
    printf("\n=== EXAMPLE 7: free() and Dangling Pointers ===\n\n");
    
    int *ptr = (int *)malloc(sizeof(int));
    if (ptr == NULL) return;
    
    *ptr = 42;
    printf("Before free:\n");
    printf("  ptr = %p\n", (void *)ptr);
    printf("  *ptr = %d\n\n", *ptr);
    
    free(ptr);
    printf("After free(ptr):\n");
    printf("  ptr still contains address: %p (dangling!)\n", (void *)ptr);
    printf("  But accessing *ptr is UNDEFINED BEHAVIOR!\n\n");
    
    // Best practice: Set to NULL after freeing
    ptr = NULL;
    printf("Best practice - set to NULL after free:\n");
    printf("  ptr = %p (NULL - safe)\n", (void *)ptr);
    printf("  Now we can check: if (ptr != NULL) before use\n");
}


/* ============================================================
 * EXAMPLE 8: Common Memory Allocation Patterns
 * ============================================================ */

void example8_patterns(void) {
    printf("\n=== EXAMPLE 8: Common Patterns ===\n\n");
    
    // Pattern 1: Allocate and check immediately
    printf("Pattern 1: Allocate + Check + Use + Free\n");
    printf("─────────────────────────────────────────\n");
    
    int *data = (int *)malloc(10 * sizeof(int));
    if (data == NULL) {
        fprintf(stderr, "Error: Memory allocation failed\n");
        return;
    }
    
    // Use the memory
    for (int i = 0; i < 10; i++) {
        data[i] = i * 2;
    }
    
    printf("  Allocated and used 10 integers\n");
    
    // Clean up
    free(data);
    data = NULL;
    printf("  Freed and nullified pointer\n\n");
    
    // Pattern 2: Dynamic string
    printf("Pattern 2: Dynamic String\n");
    printf("─────────────────────────────────────────\n");
    
    const char *source = "Hello, Dynamic Memory!";
    size_t len = strlen(source) + 1;  // +1 for null terminator
    
    char *str = (char *)malloc(len);
    if (str) {
        strcpy(str, source);
        printf("  Dynamic string: \"%s\"\n", str);
        printf("  Length: %zu (including null)\n", len);
        free(str);
    }
    
    // Pattern 3: Array of structures
    printf("\nPattern 3: Array of Structures\n");
    printf("─────────────────────────────────────────\n");
    
    typedef struct {
        int id;
        char name[20];
    } Student;
    
    int count = 3;
    Student *students = (Student *)malloc(count * sizeof(Student));
    
    if (students) {
        students[0] = (Student){1, "Alice"};
        students[1] = (Student){2, "Bob"};
        students[2] = (Student){3, "Charlie"};
        
        for (int i = 0; i < count; i++) {
            printf("  Student %d: %s\n", students[i].id, students[i].name);
        }
        
        free(students);
    }
}


/* ============================================================
 * EXAMPLE 9: Dynamic 2D Array Allocation
 * ============================================================ */

void example9_2d_array(void) {
    printf("\n=== EXAMPLE 9: Dynamic 2D Array ===\n\n");
    
    int rows = 3, cols = 4;
    
    // Method 1: Array of pointers
    printf("Method 1: Array of Pointers (rows Ɨ cols = %d Ɨ %d)\n", rows, cols);
    printf("───────────────────────────────────────────────────\n");
    
    // Allocate array of row pointers
    int **matrix = (int **)malloc(rows * sizeof(int *));
    if (matrix == NULL) return;
    
    // Allocate each row
    for (int i = 0; i < rows; i++) {
        matrix[i] = (int *)malloc(cols * sizeof(int));
        if (matrix[i] == NULL) {
            // Cleanup on failure
            for (int j = 0; j < i; j++) {
                free(matrix[j]);
            }
            free(matrix);
            return;
        }
    }
    
    // Initialize with values
    int value = 1;
    for (int i = 0; i < rows; i++) {
        for (int j = 0; j < cols; j++) {
            matrix[i][j] = value++;
        }
    }
    
    // Display
    printf("\nMatrix contents:\n");
    for (int i = 0; i < rows; i++) {
        printf("  ");
        for (int j = 0; j < cols; j++) {
            printf("%3d ", matrix[i][j]);
        }
        printf("\n");
    }
    
    // Free memory (reverse order)
    for (int i = 0; i < rows; i++) {
        free(matrix[i]);
    }
    free(matrix);
    
    printf("\nMemory freed successfully.\n");
    
    // Method 2: Single contiguous block
    printf("\nMethod 2: Single Contiguous Block\n");
    printf("───────────────────────────────────────────────────\n");
    
    int *flat = (int *)malloc(rows * cols * sizeof(int));
    if (flat == NULL) return;
    
    // Initialize
    for (int i = 0; i < rows * cols; i++) {
        flat[i] = i + 1;
    }
    
    // Access as 2D: flat[i * cols + j]
    printf("\nMatrix (single block, accessed as 2D):\n");
    for (int i = 0; i < rows; i++) {
        printf("  ");
        for (int j = 0; j < cols; j++) {
            printf("%3d ", flat[i * cols + j]);
        }
        printf("\n");
    }
    
    free(flat);
    printf("\nAdvantage: Single allocation, better cache locality.\n");
}


/* ============================================================
 * EXAMPLE 10: Dynamic Array Growth (Like Vector)
 * ============================================================ */

typedef struct {
    int *data;
    size_t size;      // Current number of elements
    size_t capacity;  // Total allocated capacity
} DynamicArray;

DynamicArray* da_create(size_t initial_capacity) {
    DynamicArray *da = (DynamicArray *)malloc(sizeof(DynamicArray));
    if (da == NULL) return NULL;
    
    da->data = (int *)malloc(initial_capacity * sizeof(int));
    if (da->data == NULL) {
        free(da);
        return NULL;
    }
    
    da->size = 0;
    da->capacity = initial_capacity;
    return da;
}

int da_push(DynamicArray *da, int value) {
    if (da->size >= da->capacity) {
        // Double the capacity
        size_t new_capacity = da->capacity * 2;
        int *new_data = (int *)realloc(da->data, new_capacity * sizeof(int));
        
        if (new_data == NULL) return 0;  // Failed
        
        da->data = new_data;
        da->capacity = new_capacity;
    }
    
    da->data[da->size++] = value;
    return 1;  // Success
}

void da_destroy(DynamicArray *da) {
    if (da) {
        free(da->data);
        free(da);
    }
}

void example10_dynamic_growth(void) {
    printf("\n=== EXAMPLE 10: Dynamic Array Growth ===\n\n");
    
    printf("Creating a vector-like dynamic array:\n\n");
    
    DynamicArray *arr = da_create(2);  // Start with capacity 2
    if (arr == NULL) return;
    
    printf("Initial: size=0, capacity=2\n\n");
    
    // Add elements and watch it grow
    for (int i = 1; i <= 10; i++) {
        size_t old_cap = arr->capacity;
        da_push(arr, i * 10);
        
        if (arr->capacity > old_cap) {
            printf("Pushed %3d → size=%zu, capacity GREW: %zu → %zu\n", 
                   i * 10, arr->size, old_cap, arr->capacity);
        } else {
            printf("Pushed %3d → size=%zu, capacity=%zu\n", 
                   i * 10, arr->size, arr->capacity);
        }
    }
    
    printf("\nFinal array contents: ");
    for (size_t i = 0; i < arr->size; i++) {
        printf("%d ", arr->data[i]);
    }
    printf("\n");
    
    da_destroy(arr);
    printf("\nDynamic array destroyed.\n");
}


/* ============================================================
 * EXAMPLE 11: Memory Leak Demonstration
 * ============================================================ */

void memory_leak_example(void) {
    printf("\n=== EXAMPLE 11: Memory Leaks (What NOT to Do) ===\n\n");
    
    printf("MEMORY LEAK SCENARIOS (DON'T DO THESE!):\n\n");
    
    printf("Scenario 1: Forgetting to free\n");
    printf("─────────────────────────────────────────\n");
    printf("  void leaky_function() {\n");
    printf("      int *ptr = malloc(100);\n");
    printf("      // ... use ptr ...\n");
    printf("      // Oops! Forgot free(ptr);\n");
    printf("  }  // Memory leaked!\n\n");
    
    printf("Scenario 2: Losing the pointer\n");
    printf("─────────────────────────────────────────\n");
    printf("  int *ptr = malloc(100);\n");
    printf("  ptr = malloc(200);  // Lost reference to first block!\n");
    printf("  // First 100 bytes are now unreachable\n\n");
    
    printf("Scenario 3: Early return without cleanup\n");
    printf("─────────────────────────────────────────\n");
    printf("  int *ptr = malloc(100);\n");
    printf("  if (some_error) {\n");
    printf("      return;  // Leak! Didn't free ptr\n");
    printf("  }\n");
    printf("  free(ptr);\n\n");
    
    printf("CORRECT PATTERN:\n");
    printf("─────────────────────────────────────────\n");
    printf("  int *ptr = malloc(100);\n");
    printf("  if (ptr == NULL) return;\n");
    printf("  \n");
    printf("  if (some_error) {\n");
    printf("      free(ptr);  // Clean up before return!\n");
    printf("      return;\n");
    printf("  }\n");
    printf("  \n");
    printf("  // ... use ptr ...\n");
    printf("  free(ptr);\n");
    printf("  ptr = NULL;\n");
}


/* ============================================================
 * EXAMPLE 12: Safe Memory Functions
 * ============================================================ */

// Safe malloc with error handling
void* safe_malloc(size_t size) {
    void *ptr = malloc(size);
    if (ptr == NULL) {
        fprintf(stderr, "Fatal: malloc(%zu) failed\n", size);
        exit(EXIT_FAILURE);
    }
    return ptr;
}

// Safe calloc with error handling
void* safe_calloc(size_t count, size_t size) {
    void *ptr = calloc(count, size);
    if (ptr == NULL) {
        fprintf(stderr, "Fatal: calloc(%zu, %zu) failed\n", count, size);
        exit(EXIT_FAILURE);
    }
    return ptr;
}

// Safe realloc with error handling
void* safe_realloc(void *ptr, size_t size) {
    void *new_ptr = realloc(ptr, size);
    if (new_ptr == NULL && size != 0) {
        fprintf(stderr, "Fatal: realloc(%zu) failed\n", size);
        exit(EXIT_FAILURE);
    }
    return new_ptr;
}

void example12_safe_functions(void) {
    printf("\n=== EXAMPLE 12: Safe Memory Functions ===\n\n");
    
    printf("Using wrapper functions for safety:\n\n");
    
    printf("void* safe_malloc(size_t size) {\n");
    printf("    void *ptr = malloc(size);\n");
    printf("    if (ptr == NULL) {\n");
    printf("        fprintf(stderr, \"Fatal: malloc failed\\n\");\n");
    printf("        exit(EXIT_FAILURE);\n");
    printf("    }\n");
    printf("    return ptr;\n");
    printf("}\n\n");
    
    // Demonstrate usage
    int *arr = (int *)safe_malloc(5 * sizeof(int));
    
    for (int i = 0; i < 5; i++) {
        arr[i] = i + 1;
    }
    
    printf("Used safe_malloc to allocate 5 integers: ");
    for (int i = 0; i < 5; i++) {
        printf("%d ", arr[i]);
    }
    printf("\n");
    
    free(arr);
}


/* ============================================================
 * EXAMPLE 13: Allocating Structures Dynamically
 * ============================================================ */

typedef struct {
    char *name;
    int age;
    double salary;
} Employee;

Employee* create_employee(const char *name, int age, double salary) {
    Employee *emp = (Employee *)malloc(sizeof(Employee));
    if (emp == NULL) return NULL;
    
    emp->name = (char *)malloc(strlen(name) + 1);
    if (emp->name == NULL) {
        free(emp);
        return NULL;
    }
    
    strcpy(emp->name, name);
    emp->age = age;
    emp->salary = salary;
    
    return emp;
}

void destroy_employee(Employee *emp) {
    if (emp) {
        free(emp->name);  // Free inner allocation first
        free(emp);        // Then free the structure
    }
}

void example13_dynamic_structs(void) {
    printf("\n=== EXAMPLE 13: Dynamic Structures ===\n\n");
    
    Employee *emp1 = create_employee("Alice Johnson", 30, 75000.0);
    Employee *emp2 = create_employee("Bob Smith", 25, 55000.0);
    
    if (emp1 && emp2) {
        printf("Employee 1:\n");
        printf("  Name: %s\n", emp1->name);
        printf("  Age: %d\n", emp1->age);
        printf("  Salary: $%.2f\n\n", emp1->salary);
        
        printf("Employee 2:\n");
        printf("  Name: %s\n", emp2->name);
        printf("  Age: %d\n", emp2->age);
        printf("  Salary: $%.2f\n", emp2->salary);
    }
    
    destroy_employee(emp1);
    destroy_employee(emp2);
    
    printf("\nBoth employees freed (including their names).\n");
}


/* ============================================================
 * EXAMPLE 14: Linked List with Dynamic Memory
 * ============================================================ */

typedef struct Node {
    int data;
    struct Node *next;
} Node;

Node* create_node(int data) {
    Node *node = (Node *)malloc(sizeof(Node));
    if (node) {
        node->data = data;
        node->next = NULL;
    }
    return node;
}

void append_node(Node **head, int data) {
    Node *new_node = create_node(data);
    if (new_node == NULL) return;
    
    if (*head == NULL) {
        *head = new_node;
        return;
    }
    
    Node *current = *head;
    while (current->next != NULL) {
        current = current->next;
    }
    current->next = new_node;
}

void print_list(Node *head) {
    Node *current = head;
    while (current != NULL) {
        printf("%d", current->data);
        if (current->next != NULL) {
            printf(" -> ");
        }
        current = current->next;
    }
    printf(" -> NULL\n");
}

void free_list(Node *head) {
    Node *current = head;
    while (current != NULL) {
        Node *temp = current;
        current = current->next;
        free(temp);
    }
}

void example14_linked_list(void) {
    printf("\n=== EXAMPLE 14: Linked List ===\n\n");
    
    Node *head = NULL;
    
    // Build the list
    printf("Building linked list...\n");
    append_node(&head, 10);
    append_node(&head, 20);
    append_node(&head, 30);
    append_node(&head, 40);
    append_node(&head, 50);
    
    printf("List: ");
    print_list(head);
    
    // Count nodes
    int count = 0;
    Node *temp = head;
    while (temp) {
        count++;
        temp = temp->next;
    }
    printf("Total nodes: %d\n", count);
    
    // Free the list
    free_list(head);
    head = NULL;
    
    printf("List freed successfully.\n");
}


/* ============================================================
 * EXAMPLE 15: Memory Alignment and Size
 * ============================================================ */

void example15_alignment(void) {
    printf("\n=== EXAMPLE 15: Memory Alignment ===\n\n");
    
    printf("Understanding memory allocation sizes:\n\n");
    
    printf("Type               sizeof    Typical Alignment\n");
    printf("─────────────────────────────────────────────────\n");
    printf("char               %zu bytes   1 byte\n", sizeof(char));
    printf("short              %zu bytes   2 bytes\n", sizeof(short));
    printf("int                %zu bytes   4 bytes\n", sizeof(int));
    printf("long               %zu bytes   %zu bytes\n", sizeof(long), sizeof(long));
    printf("long long          %zu bytes   8 bytes\n", sizeof(long long));
    printf("float              %zu bytes   4 bytes\n", sizeof(float));
    printf("double             %zu bytes   8 bytes\n", sizeof(double));
    printf("pointer (void*)    %zu bytes   %zu bytes\n", sizeof(void*), sizeof(void*));
    
    printf("\nStructure padding example:\n");
    
    struct Padded {
        char c;     // 1 byte + 3 padding
        int i;      // 4 bytes
        char d;     // 1 byte + 3 padding
    };
    
    struct Compact {
        int i;      // 4 bytes
        char c;     // 1 byte
        char d;     // 1 byte + 2 padding
    };
    
    printf("struct { char c; int i; char d; } = %zu bytes (with padding)\n", 
           sizeof(struct Padded));
    printf("struct { int i; char c; char d; } = %zu bytes (reordered)\n", 
           sizeof(struct Compact));
    
    printf("\nTip: Order struct members by size (largest first) to minimize padding.\n");
}


/* ============================================================
 * MAIN FUNCTION
 * ============================================================ */

int main() {
    printf("╔═══════════════════════════════════════════════════════════════╗\n");
    printf("ā•‘       DYNAMIC MEMORY ALLOCATION - EXAMPLES                    ā•‘\n");
    printf("ā•šā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•\n\n");
    
    example1_malloc_basics();
    example2_malloc_garbage();
    example3_calloc();
    example4_malloc_vs_calloc();
    example5_realloc();
    example6_realloc_special();
    example7_free_dangling();
    example8_patterns();
    example9_2d_array();
    example10_dynamic_growth();
    memory_leak_example();
    example12_safe_functions();
    example13_dynamic_structs();
    example14_linked_list();
    example15_alignment();
    
    printf("\n═══════════════════════════════════════════════════════════════\n");
    printf("All examples completed successfully!\n");
    printf("═══════════════════════════════════════════════════════════════\n");
    
    return 0;
}
Examples - C Programming Tutorial | DeepML