c

exercises

exercises.c🔧
/**
 * =============================================================================
 * Memory Leaks and Debugging in C - Exercises
 * =============================================================================
 * 
 * Practice exercises for memory debugging and leak prevention.
 * 
 * Compilation: gcc -g -O0 -o exercises exercises.c -Wall -Wextra
 * 
 * For Valgrind testing:
 *   gcc -g -O0 -o exercises exercises.c
 *   valgrind --leak-check=full ./exercises
 * 
 * Topics covered:
 * - Finding and fixing memory leaks
 * - Proper cleanup patterns
 * - Memory debugging wrappers
 * - Safe memory practices
 * =============================================================================
 */

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

/* ==========================================================================
 * Exercise 1: Find the Memory Leak
 * ==========================================================================
 * The function below has a memory leak. Find and fix it.
 * 
 * Expected Output:
 *   Array sum: 55
 *   Memory properly freed!
 */

int calculate_sum_buggy(int n) {
    int *arr = malloc(n * sizeof(int));
    if (!arr) return -1;
    
    for (int i = 0; i < n; i++) {
        arr[i] = i + 1;
    }
    
    int sum = 0;
    for (int i = 0; i < n; i++) {
        sum += arr[i];
    }
    
    return sum;  /* BUG: Memory leaked here! */
}

/* TODO: Write the fixed version */
int calculate_sum_fixed(int n) {
    /* Your implementation here */
    return 0;
}

void exercise_01(void) {
    printf("Exercise 1: Find the Memory Leak\n");
    printf("---------------------------------\n");
    
    /* Uncomment to test your fixed version */
    /*
    int sum = calculate_sum_fixed(10);
    printf("Array sum: %d\n", sum);
    printf("Memory properly freed!\n");
    */
    
    printf("TODO: Implement calculate_sum_fixed()\n");
    printf("\n");
}

/* ==========================================================================
 * Exercise 2: Fix Early Return Leak
 * ==========================================================================
 * This function has multiple memory leaks on error paths.
 * Fix all the leaks using proper cleanup.
 * 
 * Expected Output:
 *   Error handled with no memory leaks!
 */

typedef struct {
    char *name;
    int *data;
    float *values;
} Resource;

Resource *create_resource_buggy(const char *name, int data_count, 
                                 int value_count) {
    Resource *res = malloc(sizeof(Resource));
    if (!res) return NULL;
    
    res->name = malloc(strlen(name) + 1);
    if (!res->name) {
        return NULL;  /* BUG: res leaked! */
    }
    strcpy(res->name, name);
    
    res->data = malloc(data_count * sizeof(int));
    if (!res->data) {
        return NULL;  /* BUG: res and res->name leaked! */
    }
    
    res->values = malloc(value_count * sizeof(float));
    if (!res->values) {
        return NULL;  /* BUG: res, res->name, res->data leaked! */
    }
    
    return res;
}

/* TODO: Write the fixed version */
Resource *create_resource_fixed(const char *name, int data_count, 
                                 int value_count) {
    /* Your implementation here */
    return NULL;
}

void destroy_resource(Resource *res) {
    /* TODO: Implement proper cleanup */
}

void exercise_02(void) {
    printf("Exercise 2: Fix Early Return Leak\n");
    printf("----------------------------------\n");
    
    /* Uncomment to test your fixed version */
    /*
    Resource *res = create_resource_fixed("Test", 10, 5);
    if (res) {
        printf("Resource created successfully\n");
        destroy_resource(res);
        printf("Resource destroyed properly!\n");
    }
    */
    
    printf("TODO: Implement create_resource_fixed() and destroy_resource()\n");
    printf("\n");
}

/* ==========================================================================
 * Exercise 3: Implement Memory Tracking
 * ==========================================================================
 * Implement simple memory tracking functions that count allocations
 * and detect leaks.
 * 
 * Expected Output:
 *   Allocated 3 blocks
 *   Freed 3 blocks
 *   No leaks detected!
 */

/* Global counters */
static int g_alloc_count = 0;
static int g_free_count = 0;

/* TODO: Implement these functions */
void *tracked_malloc(size_t size) {
    /* Allocate memory and increment counter */
    return NULL;
}

void tracked_free(void *ptr) {
    /* Free memory and increment counter */
}

void print_tracking_report(void) {
    /* Print allocation/free counts and detect leaks */
}

void exercise_03(void) {
    printf("Exercise 3: Implement Memory Tracking\n");
    printf("--------------------------------------\n");
    
    /* Uncomment to test your implementation */
    /*
    int *a = tracked_malloc(sizeof(int));
    int *b = tracked_malloc(sizeof(int));
    int *c = tracked_malloc(sizeof(int));
    
    tracked_free(a);
    tracked_free(b);
    tracked_free(c);
    
    print_tracking_report();
    */
    
    printf("TODO: Implement tracked_malloc(), tracked_free(), print_tracking_report()\n");
    printf("\n");
}

/* ==========================================================================
 * Exercise 4: Linked List Cleanup
 * ==========================================================================
 * The given linked list cleanup function is buggy. Fix it.
 * 
 * Expected Output:
 *   Created list: 1 -> 2 -> 3 -> 4 -> 5 -> NULL
 *   All nodes freed successfully!
 */

typedef struct ListNode {
    int value;
    struct ListNode *next;
} ListNode;

ListNode *create_list(int count) {
    if (count <= 0) return NULL;
    
    ListNode *head = malloc(sizeof(ListNode));
    if (!head) return NULL;
    head->value = 1;
    head->next = NULL;
    
    ListNode *current = head;
    for (int i = 2; i <= count; i++) {
        current->next = malloc(sizeof(ListNode));
        if (!current->next) return head;  /* Partial list */
        current = current->next;
        current->value = i;
        current->next = NULL;
    }
    
    return head;
}

void print_list(ListNode *head) {
    while (head) {
        printf("%d -> ", head->value);
        head = head->next;
    }
    printf("NULL\n");
}

/* BUGGY: This function has problems */
void free_list_buggy(ListNode *head) {
    while (head) {
        free(head);      /* BUG: head->next lost! */
        head = head->next;  /* Accessing freed memory! */
    }
}

/* TODO: Write the fixed version */
void free_list_fixed(ListNode *head) {
    /* Your implementation here */
}

void exercise_04(void) {
    printf("Exercise 4: Linked List Cleanup\n");
    printf("--------------------------------\n");
    
    /* Uncomment to test your fixed version */
    /*
    ListNode *list = create_list(5);
    printf("Created list: ");
    print_list(list);
    
    free_list_fixed(list);
    printf("All nodes freed successfully!\n");
    */
    
    printf("TODO: Implement free_list_fixed()\n");
    printf("\n");
}

/* ==========================================================================
 * Exercise 5: Safe Free Macro
 * ==========================================================================
 * Implement a SAFE_FREE macro that frees memory and sets pointer to NULL.
 * 
 * Expected Output:
 *   Before free: ptr = 0x...
 *   After SAFE_FREE: ptr = (nil)
 *   Double free safe: no crash!
 */

/* TODO: Define SAFE_FREE macro */
/* #define SAFE_FREE(ptr) ... */

void exercise_05(void) {
    printf("Exercise 5: Safe Free Macro\n");
    printf("----------------------------\n");
    
    /* Uncomment to test your macro */
    /*
    int *ptr = malloc(sizeof(int));
    *ptr = 42;
    printf("Before free: ptr = %p\n", (void *)ptr);
    
    SAFE_FREE(ptr);
    printf("After SAFE_FREE: ptr = %p\n", (void *)ptr);
    
    // This should be safe now
    SAFE_FREE(ptr);
    printf("Double free safe: no crash!\n");
    */
    
    printf("TODO: Define SAFE_FREE macro\n");
    printf("\n");
}

/* ==========================================================================
 * Exercise 6: String Concatenation Without Leaks
 * ==========================================================================
 * Implement a function that concatenates multiple strings without leaks.
 * 
 * Expected Output:
 *   Result: Hello World!
 *   Memory freed properly!
 */

/* BUGGY version - has leaks */
char *concat_strings_buggy(const char *s1, const char *s2, const char *s3) {
    char *temp = malloc(strlen(s1) + strlen(s2) + 1);
    strcpy(temp, s1);
    strcat(temp, s2);
    
    char *result = malloc(strlen(temp) + strlen(s3) + 1);
    strcpy(result, temp);
    strcat(result, s3);
    
    return result;  /* BUG: temp is leaked! */
}

/* TODO: Write the fixed version */
char *concat_strings_fixed(const char *s1, const char *s2, const char *s3) {
    /* Your implementation here */
    return NULL;
}

void exercise_06(void) {
    printf("Exercise 6: String Concatenation Without Leaks\n");
    printf("-----------------------------------------------\n");
    
    /* Uncomment to test your fixed version */
    /*
    char *result = concat_strings_fixed("Hello", " ", "World!");
    if (result) {
        printf("Result: %s\n", result);
        free(result);
        printf("Memory freed properly!\n");
    }
    */
    
    printf("TODO: Implement concat_strings_fixed()\n");
    printf("\n");
}

/* ==========================================================================
 * Exercise 7: Implement Arena Allocator
 * ==========================================================================
 * Implement a simple arena allocator that allocates from a fixed buffer.
 * No individual frees - just reset or destroy the whole arena.
 * 
 * Expected Output:
 *   Allocated 3 integers from arena
 *   Arena used: 24 bytes
 *   Arena reset, used: 0 bytes
 */

typedef struct {
    char *buffer;
    size_t size;
    size_t used;
} SimpleArena;

/* TODO: Implement these functions */
SimpleArena *arena_create(size_t size) {
    return NULL;
}

void *arena_alloc(SimpleArena *arena, size_t size) {
    return NULL;
}

void arena_reset(SimpleArena *arena) {
}

void arena_destroy(SimpleArena *arena) {
}

void exercise_07(void) {
    printf("Exercise 7: Implement Arena Allocator\n");
    printf("--------------------------------------\n");
    
    /* Uncomment to test your implementation */
    /*
    SimpleArena *arena = arena_create(1024);
    if (!arena) {
        printf("Failed to create arena\n");
        return;
    }
    
    int *a = arena_alloc(arena, sizeof(int));
    int *b = arena_alloc(arena, sizeof(int));
    int *c = arena_alloc(arena, sizeof(int));
    
    if (a && b && c) {
        *a = 10; *b = 20; *c = 30;
        printf("Allocated 3 integers from arena\n");
        printf("Arena used: %zu bytes\n", arena->used);
    }
    
    arena_reset(arena);
    printf("Arena reset, used: %zu bytes\n", arena->used);
    
    arena_destroy(arena);
    */
    
    printf("TODO: Implement arena functions\n");
    printf("\n");
}

/* ==========================================================================
 * Exercise 8: Fix the Double Free
 * ==========================================================================
 * The code below has a double-free bug. Find and fix it.
 * 
 * Expected Output:
 *   Array processed successfully!
 *   No double-free occurred!
 */

void process_array_buggy(int *arr, int size) {
    for (int i = 0; i < size; i++) {
        arr[i] *= 2;
    }
    free(arr);  /* BUG: Function shouldn't free caller's memory */
}

/* TODO: Write the fixed version (decide on ownership) */
void process_array_fixed(int *arr, int size) {
    /* Your implementation here */
}

void exercise_08(void) {
    printf("Exercise 8: Fix the Double Free\n");
    printf("---------------------------------\n");
    
    /* Uncomment to test your fixed version */
    /*
    int *arr = malloc(5 * sizeof(int));
    for (int i = 0; i < 5; i++) arr[i] = i + 1;
    
    process_array_fixed(arr, 5);
    printf("Array processed successfully!\n");
    
    free(arr);  // Caller frees (or remove if function should own it)
    printf("No double-free occurred!\n");
    */
    
    printf("TODO: Implement process_array_fixed() with clear ownership\n");
    printf("\n");
}

/* ==========================================================================
 * Exercise 9: Implement Reference Counting
 * ==========================================================================
 * Implement reference counting for a shared buffer.
 * 
 * Expected Output:
 *   Created shared buffer, refs=1
 *   After acquire, refs=2
 *   After release, refs=1
 *   After final release: buffer freed
 */

typedef struct {
    char *data;
    int ref_count;
} SharedBuffer;

/* TODO: Implement these functions */
SharedBuffer *sharedbuf_create(const char *initial_data) {
    return NULL;
}

SharedBuffer *sharedbuf_acquire(SharedBuffer *buf) {
    return NULL;
}

void sharedbuf_release(SharedBuffer *buf) {
}

void exercise_09(void) {
    printf("Exercise 9: Implement Reference Counting\n");
    printf("-----------------------------------------\n");
    
    /* Uncomment to test your implementation */
    /*
    SharedBuffer *buf = sharedbuf_create("Shared Data");
    printf("Created shared buffer, refs=%d\n", buf->ref_count);
    
    SharedBuffer *ref = sharedbuf_acquire(buf);
    printf("After acquire, refs=%d\n", buf->ref_count);
    
    sharedbuf_release(ref);
    printf("After release, refs=%d\n", buf->ref_count);
    
    sharedbuf_release(buf);
    printf("After final release: buffer freed\n");
    */
    
    printf("TODO: Implement reference counting functions\n");
    printf("\n");
}

/* ==========================================================================
 * Exercise 10: Memory Pool Implementation
 * ==========================================================================
 * Implement a fixed-size object pool.
 * 
 * Expected Output:
 *   Pool created with 8 slots
 *   Acquired 3 objects
 *   Released 1 object
 *   Free slots: 6
 */

#define POOL_SIZE 8

typedef struct {
    int data;
    int in_use;
} PoolItem;

typedef struct {
    PoolItem items[POOL_SIZE];
    int free_count;
} ItemPool;

/* TODO: Implement these functions */
ItemPool *pool_create(void) {
    return NULL;
}

PoolItem *pool_acquire(ItemPool *pool) {
    return NULL;
}

void pool_release(ItemPool *pool, PoolItem *item) {
}

void pool_destroy(ItemPool *pool) {
}

void exercise_10(void) {
    printf("Exercise 10: Memory Pool Implementation\n");
    printf("----------------------------------------\n");
    
    /* Uncomment to test your implementation */
    /*
    ItemPool *pool = pool_create();
    printf("Pool created with %d slots\n", POOL_SIZE);
    
    PoolItem *a = pool_acquire(pool);
    PoolItem *b = pool_acquire(pool);
    PoolItem *c = pool_acquire(pool);
    printf("Acquired 3 objects\n");
    
    pool_release(pool, b);
    printf("Released 1 object\n");
    printf("Free slots: %d\n", pool->free_count);
    
    pool_release(pool, a);
    pool_release(pool, c);
    pool_destroy(pool);
    */
    
    printf("TODO: Implement pool functions\n");
    printf("\n");
}

/* ==========================================================================
 * Main Function
 * ========================================================================== */

int main(void) {
    printf("\n");
    printf("╔══════════════════════════════════════════════════════════════════╗\n");
    printf("║      Memory Leaks and Debugging in C - Exercises                 ║\n");
    printf("╚══════════════════════════════════════════════════════════════════╝\n\n");
    
    exercise_01();
    exercise_02();
    exercise_03();
    exercise_04();
    exercise_05();
    exercise_06();
    exercise_07();
    exercise_08();
    exercise_09();
    exercise_10();
    
    printf("╔══════════════════════════════════════════════════════════════════╗\n");
    printf("║                    Exercises Complete!                           ║\n");
    printf("╚══════════════════════════════════════════════════════════════════╝\n\n");
    
    return 0;
}

/* ==========================================================================
 * ANSWER KEY
 * ==========================================================================
 *
 * Exercise 1: Find the Memory Leak
 * ---------------------------------
 * int calculate_sum_fixed(int n) {
 *     int *arr = malloc(n * sizeof(int));
 *     if (!arr) return -1;
 *     
 *     for (int i = 0; i < n; i++) {
 *         arr[i] = i + 1;
 *     }
 *     
 *     int sum = 0;
 *     for (int i = 0; i < n; i++) {
 *         sum += arr[i];
 *     }
 *     
 *     free(arr);  // Fix: free before return
 *     return sum;
 * }
 *
 * Exercise 2: Fix Early Return Leak
 * ----------------------------------
 * Resource *create_resource_fixed(const char *name, int data_count, 
 *                                  int value_count) {
 *     Resource *res = malloc(sizeof(Resource));
 *     if (!res) return NULL;
 *     
 *     res->name = NULL;
 *     res->data = NULL;
 *     res->values = NULL;
 *     
 *     res->name = malloc(strlen(name) + 1);
 *     if (!res->name) goto cleanup;
 *     strcpy(res->name, name);
 *     
 *     res->data = malloc(data_count * sizeof(int));
 *     if (!res->data) goto cleanup;
 *     
 *     res->values = malloc(value_count * sizeof(float));
 *     if (!res->values) goto cleanup;
 *     
 *     return res;
 *     
 * cleanup:
 *     free(res->values);
 *     free(res->data);
 *     free(res->name);
 *     free(res);
 *     return NULL;
 * }
 * 
 * void destroy_resource(Resource *res) {
 *     if (res) {
 *         free(res->values);
 *         free(res->data);
 *         free(res->name);
 *         free(res);
 *     }
 * }
 *
 * Exercise 3: Implement Memory Tracking
 * --------------------------------------
 * void *tracked_malloc(size_t size) {
 *     void *ptr = malloc(size);
 *     if (ptr) g_alloc_count++;
 *     return ptr;
 * }
 * 
 * void tracked_free(void *ptr) {
 *     if (ptr) {
 *         g_free_count++;
 *         free(ptr);
 *     }
 * }
 * 
 * void print_tracking_report(void) {
 *     printf("Allocated %d blocks\n", g_alloc_count);
 *     printf("Freed %d blocks\n", g_free_count);
 *     if (g_alloc_count == g_free_count) {
 *         printf("No leaks detected!\n");
 *     } else {
 *         printf("WARNING: %d blocks leaked!\n", g_alloc_count - g_free_count);
 *     }
 * }
 *
 * Exercise 4: Linked List Cleanup
 * --------------------------------
 * void free_list_fixed(ListNode *head) {
 *     while (head) {
 *         ListNode *next = head->next;  // Save next before freeing
 *         free(head);
 *         head = next;
 *     }
 * }
 *
 * Exercise 5: Safe Free Macro
 * ----------------------------
 * #define SAFE_FREE(ptr) do { \
 *     free(ptr);              \
 *     (ptr) = NULL;           \
 * } while(0)
 *
 * Exercise 6: String Concatenation Without Leaks
 * -----------------------------------------------
 * char *concat_strings_fixed(const char *s1, const char *s2, const char *s3) {
 *     size_t len = strlen(s1) + strlen(s2) + strlen(s3) + 1;
 *     char *result = malloc(len);
 *     if (!result) return NULL;
 *     
 *     strcpy(result, s1);
 *     strcat(result, s2);
 *     strcat(result, s3);
 *     
 *     return result;
 * }
 * 
 * // Alternative with temp:
 * char *concat_strings_fixed_v2(const char *s1, const char *s2, const char *s3) {
 *     char *temp = malloc(strlen(s1) + strlen(s2) + 1);
 *     if (!temp) return NULL;
 *     strcpy(temp, s1);
 *     strcat(temp, s2);
 *     
 *     char *result = malloc(strlen(temp) + strlen(s3) + 1);
 *     if (!result) {
 *         free(temp);
 *         return NULL;
 *     }
 *     strcpy(result, temp);
 *     strcat(result, s3);
 *     
 *     free(temp);  // Fix: free the temporary
 *     return result;
 * }
 *
 * Exercise 7: Implement Arena Allocator
 * --------------------------------------
 * SimpleArena *arena_create(size_t size) {
 *     SimpleArena *arena = malloc(sizeof(SimpleArena));
 *     if (!arena) return NULL;
 *     
 *     arena->buffer = malloc(size);
 *     if (!arena->buffer) {
 *         free(arena);
 *         return NULL;
 *     }
 *     
 *     arena->size = size;
 *     arena->used = 0;
 *     return arena;
 * }
 * 
 * void *arena_alloc(SimpleArena *arena, size_t size) {
 *     size_t aligned = (size + 7) & ~(size_t)7;  // 8-byte align
 *     if (arena->used + aligned > arena->size) return NULL;
 *     
 *     void *ptr = arena->buffer + arena->used;
 *     arena->used += aligned;
 *     return ptr;
 * }
 * 
 * void arena_reset(SimpleArena *arena) {
 *     arena->used = 0;
 * }
 * 
 * void arena_destroy(SimpleArena *arena) {
 *     if (arena) {
 *         free(arena->buffer);
 *         free(arena);
 *     }
 * }
 *
 * Exercise 8: Fix the Double Free
 * ---------------------------------
 * // Option 1: Function doesn't own the memory
 * void process_array_fixed(int *arr, int size) {
 *     for (int i = 0; i < size; i++) {
 *         arr[i] *= 2;
 *     }
 *     // Don't free - caller owns the memory
 * }
 * 
 * // Option 2: Function takes ownership (document clearly!)
 * void process_array_and_free(int *arr, int size) {
 *     for (int i = 0; i < size; i++) {
 *         arr[i] *= 2;
 *     }
 *     free(arr);  // Function takes ownership and frees
 *     // Caller must NOT free after this
 * }
 *
 * Exercise 9: Implement Reference Counting
 * -----------------------------------------
 * SharedBuffer *sharedbuf_create(const char *initial_data) {
 *     SharedBuffer *buf = malloc(sizeof(SharedBuffer));
 *     if (!buf) return NULL;
 *     
 *     buf->data = malloc(strlen(initial_data) + 1);
 *     if (!buf->data) {
 *         free(buf);
 *         return NULL;
 *     }
 *     
 *     strcpy(buf->data, initial_data);
 *     buf->ref_count = 1;
 *     return buf;
 * }
 * 
 * SharedBuffer *sharedbuf_acquire(SharedBuffer *buf) {
 *     if (buf) buf->ref_count++;
 *     return buf;
 * }
 * 
 * void sharedbuf_release(SharedBuffer *buf) {
 *     if (buf) {
 *         buf->ref_count--;
 *         if (buf->ref_count <= 0) {
 *             free(buf->data);
 *             free(buf);
 *         }
 *     }
 * }
 *
 * Exercise 10: Memory Pool Implementation
 * ----------------------------------------
 * ItemPool *pool_create(void) {
 *     ItemPool *pool = calloc(1, sizeof(ItemPool));
 *     if (pool) {
 *         pool->free_count = POOL_SIZE;
 *     }
 *     return pool;
 * }
 * 
 * PoolItem *pool_acquire(ItemPool *pool) {
 *     if (pool->free_count == 0) return NULL;
 *     
 *     for (int i = 0; i < POOL_SIZE; i++) {
 *         if (!pool->items[i].in_use) {
 *             pool->items[i].in_use = 1;
 *             pool->items[i].data = 0;
 *             pool->free_count--;
 *             return &pool->items[i];
 *         }
 *     }
 *     return NULL;
 * }
 * 
 * void pool_release(ItemPool *pool, PoolItem *item) {
 *     if (item && item->in_use) {
 *         item->in_use = 0;
 *         pool->free_count++;
 *     }
 * }
 * 
 * void pool_destroy(ItemPool *pool) {
 *     free(pool);
 * }
 *
 * ==========================================================================
 */
Exercises - C Programming Tutorial | DeepML