c

examples

examples.c🔧
/**
 * =============================================================================
 * MALLOC() AND FREE() - EXAMPLES
 * =============================================================================
 * 
 * This file demonstrates practical usage of malloc() and free() in C.
 * 
 * Topics covered:
 * - Basic malloc and free operations
 * - Size calculations with sizeof
 * - Allocating different data types
 * - Common patterns and idioms
 * - Error handling
 * - Memory management best practices
 * 
 * Compile: gcc examples.c -o examples
 * Run: ./examples
 * =============================================================================
 */

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

/**
 * =============================================================================
 * EXAMPLE 1: Basic malloc and free
 * =============================================================================
 * Shows the simplest use of malloc and free.
 */
void example1_basic_malloc_free(void) {
    printf("=== EXAMPLE 1: Basic malloc and free ===\n\n");
    
    // -------------------------------------------------------------------------
    // Allocating a single integer
    // -------------------------------------------------------------------------
    printf("--- Allocating Single Integer ---\n");
    
    // Method 1: With explicit cast
    int *p1 = (int*)malloc(sizeof(int));
    
    // Method 2: Without cast (valid in C)
    int *p2 = malloc(sizeof(int));
    
    // Method 3: Using sizeof on the variable (RECOMMENDED)
    int *p3 = malloc(sizeof(*p3));
    
    // Always check for NULL
    if (p1 == NULL || p2 == NULL || p3 == NULL) {
        printf("Memory allocation failed!\n");
        // Clean up any successful allocations
        free(p1);
        free(p2);
        free(p3);
        return;
    }
    
    // Use the allocated memory
    *p1 = 100;
    *p2 = 200;
    *p3 = 300;
    
    printf("p1 = %d (at %p)\n", *p1, (void*)p1);
    printf("p2 = %d (at %p)\n", *p2, (void*)p2);
    printf("p3 = %d (at %p)\n", *p3, (void*)p3);
    printf("\n");
    
    // Free the memory
    free(p1);
    free(p2);
    free(p3);
    
    // Set to NULL (good practice)
    p1 = p2 = p3 = NULL;
    
    printf("Memory freed and pointers set to NULL\n\n");
}

/**
 * =============================================================================
 * EXAMPLE 2: sizeof Operator with malloc
 * =============================================================================
 * Demonstrates correct use of sizeof for portable code.
 */
void example2_sizeof_usage(void) {
    printf("=== EXAMPLE 2: sizeof Operator with malloc ===\n\n");
    
    // -------------------------------------------------------------------------
    // Size of fundamental types
    // -------------------------------------------------------------------------
    printf("--- Sizes of Fundamental Types ---\n");
    printf("sizeof(char)      = %zu bytes\n", sizeof(char));
    printf("sizeof(short)     = %zu bytes\n", sizeof(short));
    printf("sizeof(int)       = %zu bytes\n", sizeof(int));
    printf("sizeof(long)      = %zu bytes\n", sizeof(long));
    printf("sizeof(long long) = %zu bytes\n", sizeof(long long));
    printf("sizeof(float)     = %zu bytes\n", sizeof(float));
    printf("sizeof(double)    = %zu bytes\n", sizeof(double));
    printf("sizeof(void*)     = %zu bytes\n", sizeof(void*));
    printf("\n");
    
    // -------------------------------------------------------------------------
    // Correct sizeof usage with malloc
    // -------------------------------------------------------------------------
    printf("--- Correct sizeof Usage ---\n");
    
    // Allocating different types
    char *c = malloc(sizeof(*c));       // 1 byte
    short *s = malloc(sizeof(*s));      // 2 bytes typically
    int *i = malloc(sizeof(*i));        // 4 bytes typically
    double *d = malloc(sizeof(*d));     // 8 bytes typically
    
    if (c && s && i && d) {
        printf("Allocated char at %p (%zu bytes)\n", (void*)c, sizeof(*c));
        printf("Allocated short at %p (%zu bytes)\n", (void*)s, sizeof(*s));
        printf("Allocated int at %p (%zu bytes)\n", (void*)i, sizeof(*i));
        printf("Allocated double at %p (%zu bytes)\n", (void*)d, sizeof(*d));
    }
    
    free(c);
    free(s);
    free(i);
    free(d);
    printf("\n");
    
    // -------------------------------------------------------------------------
    // Why sizeof(*ptr) is better
    // -------------------------------------------------------------------------
    printf("--- Why sizeof(*ptr) is Better ---\n");
    
    // If you change the type, only ONE place to update:
    long *value = malloc(sizeof(*value));
    // Change 'long' to 'double' above, sizeof adjusts automatically
    
    // Less maintainable: type appears twice
    long *value2 = malloc(sizeof(long));
    // If you change 'long' to 'double' in declaration,
    // you must also change the sizeof - easy to forget!
    
    if (value && value2) {
        *value = 12345L;
        *value2 = 67890L;
        printf("Using sizeof(*ptr): %ld\n", *value);
        printf("Using sizeof(type): %ld\n", *value2);
    }
    
    free(value);
    free(value2);
    printf("\n");
}

/**
 * =============================================================================
 * EXAMPLE 3: Allocating Arrays
 * =============================================================================
 * Shows how to dynamically allocate arrays.
 */
void example3_array_allocation(void) {
    printf("=== EXAMPLE 3: Allocating Arrays ===\n\n");
    
    // -------------------------------------------------------------------------
    // Integer array
    // -------------------------------------------------------------------------
    printf("--- Integer Array ---\n");
    
    int n = 10;
    int *intArray = malloc(n * sizeof(*intArray));
    
    if (intArray == NULL) {
        printf("Allocation failed!\n");
        return;
    }
    
    // Fill with squares
    for (int i = 0; i < n; i++) {
        intArray[i] = i * i;
    }
    
    printf("Array of %d integers: ", n);
    for (int i = 0; i < n; i++) {
        printf("%d ", intArray[i]);
    }
    printf("\n");
    printf("Total size: %zu bytes\n\n", n * sizeof(*intArray));
    
    free(intArray);
    
    // -------------------------------------------------------------------------
    // Character array (string buffer)
    // -------------------------------------------------------------------------
    printf("--- Character Array (String Buffer) ---\n");
    
    size_t bufferSize = 100;
    char *buffer = malloc(bufferSize * sizeof(*buffer));
    
    if (buffer != NULL) {
        strcpy(buffer, "Hello, Dynamic World!");
        printf("Buffer: \"%s\"\n", buffer);
        printf("Buffer size: %zu bytes\n\n", bufferSize);
        free(buffer);
    }
    
    // -------------------------------------------------------------------------
    // Double array for calculations
    // -------------------------------------------------------------------------
    printf("--- Double Array ---\n");
    
    int numValues = 5;
    double *values = malloc(numValues * sizeof(*values));
    
    if (values != NULL) {
        // Initialize with some values
        values[0] = 1.1;
        values[1] = 2.2;
        values[2] = 3.3;
        values[3] = 4.4;
        values[4] = 5.5;
        
        // Calculate sum
        double sum = 0;
        for (int i = 0; i < numValues; i++) {
            sum += values[i];
        }
        
        printf("Values: ");
        for (int i = 0; i < numValues; i++) {
            printf("%.1f ", values[i]);
        }
        printf("\n");
        printf("Sum: %.1f\n", sum);
        printf("Total size: %zu bytes\n\n", numValues * sizeof(*values));
        
        free(values);
    }
}

/**
 * =============================================================================
 * EXAMPLE 4: Allocating Strings
 * =============================================================================
 * Demonstrates string allocation patterns.
 */
void example4_string_allocation(void) {
    printf("=== EXAMPLE 4: Allocating Strings ===\n\n");
    
    // -------------------------------------------------------------------------
    // Exact-size string allocation
    // -------------------------------------------------------------------------
    printf("--- Exact-Size String Allocation ---\n");
    
    const char *original = "Hello, World!";
    size_t length = strlen(original) + 1;  // +1 for null terminator
    
    char *copy = malloc(length);
    if (copy != NULL) {
        strcpy(copy, original);
        printf("Original: \"%s\" (static)\n", original);
        printf("Copy:     \"%s\" (dynamic, %zu bytes)\n", copy, length);
        free(copy);
    }
    printf("\n");
    
    // -------------------------------------------------------------------------
    // String concatenation
    // -------------------------------------------------------------------------
    printf("--- String Concatenation ---\n");
    
    const char *first = "Hello, ";
    const char *second = "World!";
    
    size_t totalLen = strlen(first) + strlen(second) + 1;
    char *combined = malloc(totalLen);
    
    if (combined != NULL) {
        strcpy(combined, first);
        strcat(combined, second);
        printf("First:    \"%s\"\n", first);
        printf("Second:   \"%s\"\n", second);
        printf("Combined: \"%s\" (%zu bytes)\n", combined, totalLen);
        free(combined);
    }
    printf("\n");
    
    // -------------------------------------------------------------------------
    // Building string dynamically
    // -------------------------------------------------------------------------
    printf("--- Building String with sprintf ---\n");
    
    int id = 42;
    const char *name = "Alice";
    float score = 95.5f;
    
    // Calculate needed size
    int neededSize = snprintf(NULL, 0, "ID: %d, Name: %s, Score: %.1f", 
                              id, name, score);
    
    char *formatted = malloc(neededSize + 1);  // +1 for null terminator
    
    if (formatted != NULL) {
        sprintf(formatted, "ID: %d, Name: %s, Score: %.1f", id, name, score);
        printf("Formatted: \"%s\"\n", formatted);
        printf("Size: %d bytes\n", neededSize + 1);
        free(formatted);
    }
    printf("\n");
}

/**
 * =============================================================================
 * EXAMPLE 5: Allocating Structures
 * =============================================================================
 * Shows how to allocate memory for structures.
 */
void example5_structure_allocation(void) {
    printf("=== EXAMPLE 5: Allocating Structures ===\n\n");
    
    // -------------------------------------------------------------------------
    // Simple structure
    // -------------------------------------------------------------------------
    printf("--- Simple Structure ---\n");
    
    typedef struct {
        int x;
        int y;
    } Point;
    
    Point *p = malloc(sizeof(*p));
    
    if (p != NULL) {
        p->x = 10;
        p->y = 20;
        printf("Point: (%d, %d)\n", p->x, p->y);
        printf("Size: %zu bytes\n\n", sizeof(*p));
        free(p);
    }
    
    // -------------------------------------------------------------------------
    // Structure with array member
    // -------------------------------------------------------------------------
    printf("--- Structure with Array Member ---\n");
    
    typedef struct {
        int id;
        char name[50];
        double salary;
    } Employee;
    
    Employee *emp = malloc(sizeof(*emp));
    
    if (emp != NULL) {
        emp->id = 101;
        strcpy(emp->name, "John Doe");
        emp->salary = 75000.50;
        
        printf("Employee:\n");
        printf("  ID:     %d\n", emp->id);
        printf("  Name:   %s\n", emp->name);
        printf("  Salary: $%.2f\n", emp->salary);
        printf("Size: %zu bytes\n\n", sizeof(*emp));
        
        free(emp);
    }
    
    // -------------------------------------------------------------------------
    // Structure with pointer members
    // -------------------------------------------------------------------------
    printf("--- Structure with Pointer Members ---\n");
    
    typedef struct {
        char *name;      // Pointer - needs separate allocation
        int *scores;     // Pointer - needs separate allocation
        int numScores;
    } Student;
    
    Student *s = malloc(sizeof(*s));
    
    if (s != NULL) {
        // Allocate name
        const char *studentName = "Alice Smith";
        s->name = malloc(strlen(studentName) + 1);
        if (s->name != NULL) {
            strcpy(s->name, studentName);
        }
        
        // Allocate scores array
        s->numScores = 5;
        s->scores = malloc(s->numScores * sizeof(*s->scores));
        if (s->scores != NULL) {
            int grades[] = {85, 90, 78, 92, 88};
            for (int i = 0; i < s->numScores; i++) {
                s->scores[i] = grades[i];
            }
        }
        
        // Print student info
        printf("Student:\n");
        printf("  Name:   %s\n", s->name ? s->name : "(null)");
        printf("  Scores: ");
        if (s->scores) {
            for (int i = 0; i < s->numScores; i++) {
                printf("%d ", s->scores[i]);
            }
        }
        printf("\n\n");
        
        // Free in reverse order of allocation
        free(s->scores);
        free(s->name);
        free(s);
        
        printf("Note: Free pointer members BEFORE freeing the structure!\n");
    }
    printf("\n");
}

/**
 * =============================================================================
 * EXAMPLE 6: Array of Structures
 * =============================================================================
 * Demonstrates allocating arrays of structures.
 */
void example6_array_of_structures(void) {
    printf("=== EXAMPLE 6: Array of Structures ===\n\n");
    
    typedef struct {
        int id;
        char name[30];
        float price;
    } Product;
    
    int numProducts = 3;
    
    // Allocate array of structures
    Product *products = malloc(numProducts * sizeof(*products));
    
    if (products == NULL) {
        printf("Allocation failed!\n");
        return;
    }
    
    // Initialize products
    products[0] = (Product){101, "Laptop", 999.99f};
    products[1] = (Product){102, "Mouse", 29.99f};
    products[2] = (Product){103, "Keyboard", 79.99f};
    
    // Print all products
    printf("Product List:\n");
    printf("%-6s %-20s %10s\n", "ID", "Name", "Price");
    printf("------------------------------------------\n");
    
    for (int i = 0; i < numProducts; i++) {
        printf("%-6d %-20s $%9.2f\n", 
               products[i].id, 
               products[i].name, 
               products[i].price);
    }
    
    printf("\nTotal allocation: %zu bytes\n", numProducts * sizeof(*products));
    printf("Per item: %zu bytes\n\n", sizeof(*products));
    
    free(products);
}

/**
 * =============================================================================
 * EXAMPLE 7: Error Handling Patterns
 * =============================================================================
 * Shows different approaches to handling allocation failures.
 */
void example7_error_handling(void) {
    printf("=== EXAMPLE 7: Error Handling Patterns ===\n\n");
    
    // -------------------------------------------------------------------------
    // Pattern 1: Simple NULL check
    // -------------------------------------------------------------------------
    printf("--- Pattern 1: Simple NULL Check ---\n");
    
    int *p1 = malloc(sizeof(int));
    if (p1 == NULL) {
        fprintf(stderr, "Error: Memory allocation failed\n");
        // Handle error appropriately
    } else {
        *p1 = 42;
        printf("Allocated: %d\n", *p1);
        free(p1);
    }
    printf("\n");
    
    // -------------------------------------------------------------------------
    // Pattern 2: Multiple allocations with cleanup
    // -------------------------------------------------------------------------
    printf("--- Pattern 2: Multiple Allocations with Cleanup ---\n");
    
    int *a = NULL, *b = NULL, *c = NULL;
    
    a = malloc(sizeof(*a));
    if (a == NULL) goto cleanup;
    
    b = malloc(sizeof(*b));
    if (b == NULL) goto cleanup;
    
    c = malloc(sizeof(*c));
    if (c == NULL) goto cleanup;
    
    // All allocations succeeded
    *a = 1; *b = 2; *c = 3;
    printf("All allocations succeeded: %d, %d, %d\n", *a, *b, *c);
    
cleanup:
    // free(NULL) is safe, so this always works
    free(c);
    free(b);
    free(a);
    printf("Cleanup complete\n\n");
    
    // -------------------------------------------------------------------------
    // Pattern 3: Safe wrapper function
    // -------------------------------------------------------------------------
    printf("--- Pattern 3: Safe Wrapper Function ---\n");
    printf("Implementation:\n");
    printf("  void *safe_malloc(size_t size) {\n");
    printf("      void *ptr = malloc(size);\n");
    printf("      if (ptr == NULL && size != 0) {\n");
    printf("          fprintf(stderr, \"Fatal: Out of memory\\n\");\n");
    printf("          exit(EXIT_FAILURE);\n");
    printf("      }\n");
    printf("      return ptr;\n");
    printf("  }\n\n");
}

/**
 * =============================================================================
 * EXAMPLE 8: Memory Lifecycle
 * =============================================================================
 * Traces memory through allocation, use, and deallocation.
 */
void example8_memory_lifecycle(void) {
    printf("=== EXAMPLE 8: Memory Lifecycle ===\n\n");
    
    printf("--- Step 1: Allocation ---\n");
    int *data = malloc(5 * sizeof(*data));
    printf("malloc() called: allocated %zu bytes at %p\n", 
           5 * sizeof(*data), (void*)data);
    
    if (data == NULL) {
        printf("Allocation failed!\n");
        return;
    }
    
    printf("\n--- Step 2: Initialization ---\n");
    printf("Before init (garbage values may crash if accessed):\n");
    // Note: Reading uninitialized memory is undefined behavior
    // We'll initialize immediately for safety
    
    for (int i = 0; i < 5; i++) {
        data[i] = (i + 1) * 10;
    }
    printf("After init: ");
    for (int i = 0; i < 5; i++) {
        printf("%d ", data[i]);
    }
    printf("\n");
    
    printf("\n--- Step 3: Usage ---\n");
    int sum = 0;
    for (int i = 0; i < 5; i++) {
        sum += data[i];
    }
    printf("Sum of elements: %d\n", sum);
    
    // Modify some values
    data[2] = 999;
    printf("After modification: ");
    for (int i = 0; i < 5; i++) {
        printf("%d ", data[i]);
    }
    printf("\n");
    
    printf("\n--- Step 4: Deallocation ---\n");
    printf("Calling free() on %p\n", (void*)data);
    free(data);
    printf("Memory freed\n");
    
    printf("\n--- Step 5: Pointer Cleanup ---\n");
    printf("Before: data = %p (dangling!)\n", (void*)data);
    data = NULL;
    printf("After:  data = %p (safe)\n", (void*)data);
    
    printf("\nFull lifecycle complete.\n\n");
}

/**
 * =============================================================================
 * EXAMPLE 9: What Happens After free()
 * =============================================================================
 * Demonstrates why using freed memory is dangerous.
 */
void example9_after_free(void) {
    printf("=== EXAMPLE 9: What Happens After free() ===\n\n");
    
    printf("After free():\n");
    printf("1. Memory is returned to the heap manager\n");
    printf("2. Memory may be reused for other allocations\n");
    printf("3. The pointer still holds the old address (dangling)\n");
    printf("4. The memory contents may or may not change\n");
    printf("5. Accessing freed memory is UNDEFINED BEHAVIOR\n\n");
    
    // Demonstrate with safe observation
    int *p = malloc(sizeof(int));
    if (p != NULL) {
        *p = 12345;
        printf("Before free: *p = %d, p = %p\n", *p, (void*)p);
        
        free(p);
        
        printf("After free:  p = %p (same address, but invalid!)\n", (void*)p);
        printf("             Accessing *p would be undefined behavior\n\n");
        
        // Set to NULL for safety
        p = NULL;
        printf("After NULL:  p = %p (now safe)\n", (void*)p);
    }
    
    printf("\n--- Why NULL Helps ---\n");
    printf("if (p != NULL) {\n");
    printf("    *p = 10;  // Safe: we know p is valid\n");
    printf("}\n");
    printf("If p was NULL, the check prevents the crash.\n");
    printf("If p was dangling, no check can help - bug in code!\n\n");
}

/**
 * =============================================================================
 * EXAMPLE 10: Factory Pattern
 * =============================================================================
 * Functions that allocate and return memory.
 */

// Factory function to create a string copy
char *createStringCopy(const char *source) {
    if (source == NULL) return NULL;
    
    size_t length = strlen(source) + 1;
    char *copy = malloc(length);
    
    if (copy != NULL) {
        strcpy(copy, source);
    }
    
    return copy;  // Caller must free
}

// Factory function to create an integer array
int *createIntArray(int size, int initialValue) {
    if (size <= 0) return NULL;
    
    int *array = malloc(size * sizeof(*array));
    
    if (array != NULL) {
        for (int i = 0; i < size; i++) {
            array[i] = initialValue;
        }
    }
    
    return array;  // Caller must free
}

// Factory function for a structure
typedef struct {
    int id;
    char *name;
    int age;
} PersonEx;

PersonEx *createPersonEx(int id, const char *name, int age) {
    PersonEx *p = malloc(sizeof(*p));
    if (p == NULL) return NULL;
    
    p->id = id;
    p->age = age;
    
    p->name = createStringCopy(name);
    if (p->name == NULL) {
        free(p);
        return NULL;
    }
    
    return p;  // Caller must call destroyPerson
}

void destroyPersonEx(PersonEx *p) {
    if (p != NULL) {
        free(p->name);
        free(p);
    }
}

void example10_factory_pattern(void) {
    printf("=== EXAMPLE 10: Factory Pattern ===\n\n");
    
    // -------------------------------------------------------------------------
    // Using string factory
    // -------------------------------------------------------------------------
    printf("--- String Factory ---\n");
    char *str = createStringCopy("Hello from factory!");
    if (str != NULL) {
        printf("Created: \"%s\"\n", str);
        free(str);
    }
    printf("\n");
    
    // -------------------------------------------------------------------------
    // Using array factory
    // -------------------------------------------------------------------------
    printf("--- Array Factory ---\n");
    int *arr = createIntArray(5, 42);
    if (arr != NULL) {
        printf("Created array: ");
        for (int i = 0; i < 5; i++) {
            printf("%d ", arr[i]);
        }
        printf("\n");
        free(arr);
    }
    printf("\n");
    
    // -------------------------------------------------------------------------
    // Using structure factory
    // -------------------------------------------------------------------------
    printf("--- Structure Factory ---\n");
    PersonEx *person = createPersonEx(1, "Alice", 25);
    if (person != NULL) {
        printf("Created Person:\n");
        printf("  ID:   %d\n", person->id);
        printf("  Name: %s\n", person->name);
        printf("  Age:  %d\n", person->age);
        destroyPersonEx(person);
    }
    printf("\n");
    
    // -------------------------------------------------------------------------
    // Key points
    // -------------------------------------------------------------------------
    printf("--- Factory Pattern Rules ---\n");
    printf("1. Document that caller must free/destroy\n");
    printf("2. Provide matching destroy function for complex types\n");
    printf("3. Check for NULL return to detect failures\n");
    printf("4. Handle nested allocation failures properly\n");
    printf("\n");
}

/**
 * =============================================================================
 * EXAMPLE 11: 2D Array Allocation
 * =============================================================================
 * Different approaches to allocating 2D arrays.
 */
void example11_2d_arrays(void) {
    printf("=== EXAMPLE 11: 2D Array Allocation ===\n\n");
    
    int rows = 3, cols = 4;
    
    // -------------------------------------------------------------------------
    // Method 1: Array of pointers (flexible rows)
    // -------------------------------------------------------------------------
    printf("--- Method 1: Array of Pointers ---\n");
    
    int **matrix1 = malloc(rows * sizeof(*matrix1));
    if (matrix1 != NULL) {
        // Allocate each row
        int allocFailed = 0;
        for (int i = 0; i < rows && !allocFailed; i++) {
            matrix1[i] = malloc(cols * sizeof(**matrix1));
            if (matrix1[i] == NULL) {
                allocFailed = 1;
                // Free previously allocated rows
                while (--i >= 0) free(matrix1[i]);
            }
        }
        
        if (!allocFailed) {
            // Fill with values
            int val = 1;
            for (int i = 0; i < rows; i++) {
                for (int j = 0; j < cols; j++) {
                    matrix1[i][j] = val++;
                }
            }
            
            printf("Matrix (%d x %d):\n", rows, cols);
            for (int i = 0; i < rows; i++) {
                printf("  ");
                for (int j = 0; j < cols; j++) {
                    printf("%3d ", matrix1[i][j]);
                }
                printf("\n");
            }
            
            // Free all rows, then the row pointer array
            for (int i = 0; i < rows; i++) {
                free(matrix1[i]);
            }
        }
        free(matrix1);
    }
    printf("\n");
    
    // -------------------------------------------------------------------------
    // Method 2: Contiguous block (better cache performance)
    // -------------------------------------------------------------------------
    printf("--- Method 2: Contiguous Block ---\n");
    
    int *matrix2 = malloc(rows * cols * sizeof(*matrix2));
    if (matrix2 != NULL) {
        // Fill with values (access as matrix2[i * cols + j])
        int val = 1;
        for (int i = 0; i < rows; i++) {
            for (int j = 0; j < cols; j++) {
                matrix2[i * cols + j] = val++;
            }
        }
        
        printf("Matrix (%d x %d):\n", rows, cols);
        for (int i = 0; i < rows; i++) {
            printf("  ");
            for (int j = 0; j < cols; j++) {
                printf("%3d ", matrix2[i * cols + j]);
            }
            printf("\n");
        }
        
        // Single free for entire matrix
        free(matrix2);
    }
    
    printf("\nComparison:\n");
    printf("  Array of pointers: Flexible row sizes, more memory overhead\n");
    printf("  Contiguous block:  Fixed row size, better cache performance\n");
    printf("\n");
}

/**
 * =============================================================================
 * EXAMPLE 12: Common Mistakes (Educational)
 * =============================================================================
 * Shows common mistakes and their corrections.
 */
void example12_common_mistakes(void) {
    printf("=== EXAMPLE 12: Common Mistakes (Educational) ===\n\n");
    
    // -------------------------------------------------------------------------
    // Mistake 1: Wrong sizeof
    // -------------------------------------------------------------------------
    printf("--- Mistake 1: Wrong sizeof ---\n");
    printf("WRONG: int *arr = malloc(10 * sizeof(int*));  // Pointer size!\n");
    printf("RIGHT: int *arr = malloc(10 * sizeof(int));   // Element size\n");
    printf("BEST:  int *arr = malloc(10 * sizeof(*arr));  // Automatic\n\n");
    
    // -------------------------------------------------------------------------
    // Mistake 2: Forgetting +1 for strings
    // -------------------------------------------------------------------------
    printf("--- Mistake 2: Forgetting String Terminator ---\n");
    printf("WRONG: char *s = malloc(strlen(str));        // No room for '\\0'!\n");
    printf("RIGHT: char *s = malloc(strlen(str) + 1);    // Includes '\\0'\n\n");
    
    // -------------------------------------------------------------------------
    // Mistake 3: Not checking NULL
    // -------------------------------------------------------------------------
    printf("--- Mistake 3: Not Checking NULL ---\n");
    printf("WRONG:\n");
    printf("  int *p = malloc(SIZE);\n");
    printf("  *p = 10;  // CRASH if malloc failed!\n");
    printf("RIGHT:\n");
    printf("  int *p = malloc(SIZE);\n");
    printf("  if (p == NULL) { /* handle error */ }\n");
    printf("  *p = 10;\n\n");
    
    // -------------------------------------------------------------------------
    // Mistake 4: Memory leak on reallocation
    // -------------------------------------------------------------------------
    printf("--- Mistake 4: Memory Leak with Pointer Reassignment ---\n");
    printf("WRONG:\n");
    printf("  int *p = malloc(100);\n");
    printf("  p = malloc(200);  // First allocation leaked!\n");
    printf("RIGHT:\n");
    printf("  int *p = malloc(100);\n");
    printf("  free(p);\n");
    printf("  p = malloc(200);  // Or use realloc\n\n");
    
    // -------------------------------------------------------------------------
    // Mistake 5: Using freed memory
    // -------------------------------------------------------------------------
    printf("--- Mistake 5: Using Freed Memory ---\n");
    printf("WRONG:\n");
    printf("  free(p);\n");
    printf("  printf(\"%%d\", *p);  // Undefined behavior!\n");
    printf("RIGHT:\n");
    printf("  printf(\"%%d\", *p);  // Use before free\n");
    printf("  free(p);\n");
    printf("  p = NULL;  // Prevent accidental use\n\n");
    
    // -------------------------------------------------------------------------
    // Mistake 6: Double free
    // -------------------------------------------------------------------------
    printf("--- Mistake 6: Double Free ---\n");
    printf("WRONG:\n");
    printf("  free(p);\n");
    printf("  free(p);  // Undefined behavior!\n");
    printf("RIGHT:\n");
    printf("  free(p);\n");
    printf("  p = NULL;\n");
    printf("  free(p);  // Safe: free(NULL) is no-op\n\n");
}

/**
 * =============================================================================
 * MAIN FUNCTION
 * =============================================================================
 */
int main(void) {
    printf("****************************************************************\n");
    printf("*           MALLOC() AND FREE() - EXAMPLES                     *\n");
    printf("****************************************************************\n\n");
    
    example1_basic_malloc_free();
    example2_sizeof_usage();
    example3_array_allocation();
    example4_string_allocation();
    example5_structure_allocation();
    example6_array_of_structures();
    example7_error_handling();
    example8_memory_lifecycle();
    example9_after_free();
    example10_factory_pattern();
    example11_2d_arrays();
    example12_common_mistakes();
    
    printf("****************************************************************\n");
    printf("*                    EXAMPLES COMPLETE                         *\n");
    printf("****************************************************************\n");
    
    return 0;
}
Examples - C Programming Tutorial | DeepML