c

exercises

exercises.c🔧
/**
 * =============================================================================
 * INTRODUCTION TO DYNAMIC MEMORY ALLOCATION: EXERCISES
 * =============================================================================
 * 
 * This file contains 10 exercises on dynamic memory fundamentals.
 * Topics covered:
 * - Basic malloc and free usage
 * - Proper error handling
 * - Memory for arrays and structures
 * - Understanding memory layout
 * - Avoiding common pitfalls
 * 
 * Compile: gcc exercises.c -o exercises
 * Run: ./exercises
 * 
 * To see solutions:
 * Compile: gcc exercises.c -o exercises -DSHOW_ANSWERS
 * =============================================================================
 */

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

/**
 * =============================================================================
 * EXERCISE 1: Allocate Single Integer
 * =============================================================================
 * 
 * OBJECTIVE:
 * Write a function that allocates memory for a single integer,
 * initializes it with a given value, and returns the pointer.
 * 
 * REQUIREMENTS:
 * 1. Use malloc to allocate memory for one integer
 * 2. Check if allocation succeeded
 * 3. If successful, store the value in the allocated memory
 * 4. Return the pointer (or NULL if allocation failed)
 * 
 * FUNCTION PROTOTYPE:
 * int* createInteger(int value);
 * 
 * EXAMPLE:
 * int *p = createInteger(42);
 * // *p should be 42
 * free(p);
 */

// YOUR CODE HERE
int* createInteger_exercise(int value) {
    // TODO: Implement this function
    return NULL;  // Placeholder
}

/**
 * =============================================================================
 * EXERCISE 2: Allocate Integer Array
 * =============================================================================
 * 
 * OBJECTIVE:
 * Write a function that allocates an array of integers and
 * initializes all elements to a specified value.
 * 
 * REQUIREMENTS:
 * 1. Allocate memory for 'size' integers
 * 2. Check for allocation failure
 * 3. Initialize all elements to 'initValue'
 * 4. Return the pointer (or NULL on failure)
 * 
 * FUNCTION PROTOTYPE:
 * int* createArray(int size, int initValue);
 * 
 * EXAMPLE:
 * int *arr = createArray(5, 10);
 * // arr[0] through arr[4] should all be 10
 * free(arr);
 */

// YOUR CODE HERE
int* createArray_exercise(int size, int initValue) {
    // TODO: Implement this function
    return NULL;  // Placeholder
}

/**
 * =============================================================================
 * EXERCISE 3: Duplicate String
 * =============================================================================
 * 
 * OBJECTIVE:
 * Write a function that creates a copy of a string.
 * 
 * REQUIREMENTS:
 * 1. Calculate the length of the source string (include null terminator)
 * 2. Allocate memory for the copy
 * 3. Copy the string to the new memory
 * 4. Return the new string (or NULL on failure)
 * 
 * FUNCTION PROTOTYPE:
 * char* duplicateString(const char *src);
 * 
 * EXAMPLE:
 * char *copy = duplicateString("Hello");
 * // copy should be "Hello" at a different address
 * free(copy);
 * 
 * HINT:
 * strlen() returns length without null terminator
 */

// YOUR CODE HERE
char* duplicateString_exercise(const char *src) {
    // TODO: Implement this function
    return NULL;  // Placeholder
}

/**
 * =============================================================================
 * EXERCISE 4: Concatenate Strings
 * =============================================================================
 * 
 * OBJECTIVE:
 * Write a function that concatenates two strings into a new dynamically
 * allocated string.
 * 
 * REQUIREMENTS:
 * 1. Calculate total length needed
 * 2. Allocate memory for result
 * 3. Copy first string, then second string
 * 4. Return the new concatenated string
 * 
 * FUNCTION PROTOTYPE:
 * char* concatStrings(const char *s1, const char *s2);
 * 
 * EXAMPLE:
 * char *result = concatStrings("Hello, ", "World!");
 * // result should be "Hello, World!"
 * free(result);
 */

// YOUR CODE HERE
char* concatStrings_exercise(const char *s1, const char *s2) {
    // TODO: Implement this function
    return NULL;  // Placeholder
}

/**
 * =============================================================================
 * EXERCISE 5: Allocate and Fill Structure
 * =============================================================================
 * 
 * OBJECTIVE:
 * Write a function that allocates a Person structure and fills it with
 * the provided values.
 * 
 * STRUCTURE:
 * typedef struct {
 *     char name[50];
 *     int age;
 *     float height;
 * } Person;
 * 
 * REQUIREMENTS:
 * 1. Allocate memory for one Person structure
 * 2. Copy the name (max 49 chars + null terminator)
 * 3. Set age and height
 * 4. Return pointer to Person (or NULL on failure)
 * 
 * FUNCTION PROTOTYPE:
 * Person* createPerson(const char *name, int age, float height);
 */

typedef struct {
    char name[50];
    int age;
    float height;
} Person;

// YOUR CODE HERE
Person* createPerson_exercise(const char *name, int age, float height) {
    // TODO: Implement this function
    return NULL;  // Placeholder
}

/**
 * =============================================================================
 * EXERCISE 6: Array of Structures
 * =============================================================================
 * 
 * OBJECTIVE:
 * Write a function that creates an array of Person structures,
 * all initialized with default values.
 * 
 * REQUIREMENTS:
 * 1. Allocate array of 'count' Person structures (use calloc for zeros)
 * 2. Initialize each person with:
 *    - name: "Unknown"
 *    - age: 0
 *    - height: 0.0
 * 3. Return pointer to array (or NULL on failure)
 * 
 * FUNCTION PROTOTYPE:
 * Person* createPeopleArray(int count);
 */

// YOUR CODE HERE
Person* createPeopleArray_exercise(int count) {
    // TODO: Implement this function
    return NULL;  // Placeholder
}

/**
 * =============================================================================
 * EXERCISE 7: Safe Realloc
 * =============================================================================
 * 
 * OBJECTIVE:
 * Write a function that safely reallocates an integer array.
 * If reallocation fails, the original array should be preserved.
 * 
 * REQUIREMENTS:
 * 1. Attempt to realloc to new size
 * 2. If successful, update the pointer and return 1
 * 3. If failed, keep original pointer unchanged and return 0
 * 4. Initialize new elements (if growing) to a specified value
 * 
 * FUNCTION PROTOTYPE:
 * int safeRealloc(int **arr, int oldSize, int newSize, int initValue);
 * 
 * EXAMPLE:
 * int *arr = malloc(5 * sizeof(int));
 * // ... fill arr ...
 * if (safeRealloc(&arr, 5, 10, 0)) {
 *     // Success: arr now has 10 elements
 * } else {
 *     // Failed: arr still has 5 elements
 * }
 * 
 * NOTE:
 * Must pass pointer to pointer to modify the caller's pointer
 */

// YOUR CODE HERE
int safeRealloc_exercise(int **arr, int oldSize, int newSize, int initValue) {
    // TODO: Implement this function
    return 0;  // Placeholder
}

/**
 * =============================================================================
 * EXERCISE 8: Create 2D Matrix
 * =============================================================================
 * 
 * OBJECTIVE:
 * Write a function that creates a 2D integer matrix with specified dimensions.
 * 
 * REQUIREMENTS:
 * 1. Allocate array of row pointers
 * 2. Allocate each row as separate array
 * 3. Initialize all elements to specified value
 * 4. If any allocation fails, free all previously allocated memory
 * 5. Return pointer to matrix (or NULL on failure)
 * 
 * FUNCTION PROTOTYPE:
 * int** createMatrix(int rows, int cols, int initValue);
 * 
 * Also write a function to free the matrix:
 * void freeMatrix(int **matrix, int rows);
 */

// YOUR CODE HERE
int** createMatrix_exercise(int rows, int cols, int initValue) {
    // TODO: Implement this function
    return NULL;  // Placeholder
}

void freeMatrix_exercise(int **matrix, int rows) {
    // TODO: Implement this function
}

/**
 * =============================================================================
 * EXERCISE 9: Dynamic Stack
 * =============================================================================
 * 
 * OBJECTIVE:
 * Implement a simple stack using dynamic memory.
 * 
 * STRUCTURE:
 * typedef struct {
 *     int *data;
 *     int top;      // Index of top element (-1 if empty)
 *     int capacity; // Current capacity
 * } Stack;
 * 
 * REQUIREMENTS:
 * 1. createStack(int capacity) - Create new stack
 * 2. push(Stack *s, int value) - Add element (return 1 success, 0 fail)
 *    - If full, double capacity using realloc
 * 3. pop(Stack *s, int *value) - Remove top element (return 1 success, 0 fail)
 * 4. freeStack(Stack *s) - Free all memory
 * 
 * EXAMPLE:
 * Stack *s = createStack(2);
 * push(s, 10);
 * push(s, 20);
 * push(s, 30);  // Should trigger realloc
 * int val;
 * pop(s, &val); // val = 30
 * freeStack(s);
 */

typedef struct {
    int *data;
    int top;
    int capacity;
} Stack;

// YOUR CODE HERE
Stack* createStack_exercise(int capacity) {
    // TODO: Implement this function
    return NULL;  // Placeholder
}

int push_exercise(Stack *s, int value) {
    // TODO: Implement this function
    return 0;  // Placeholder
}

int pop_exercise(Stack *s, int *value) {
    // TODO: Implement this function
    return 0;  // Placeholder
}

void freeStack_exercise(Stack *s) {
    // TODO: Implement this function
}

/**
 * =============================================================================
 * EXERCISE 10: Memory Analysis
 * =============================================================================
 * 
 * OBJECTIVE:
 * Write a function that analyzes memory addresses to determine their
 * likely location (stack, heap, data segment).
 * 
 * REQUIREMENTS:
 * This is a theoretical exercise. Implement a function that takes
 * various pointers and prints information about them.
 * 
 * FUNCTION PROTOTYPE:
 * void analyzeMemory(void *stackPtr, void *heapPtr, void *dataPtr);
 * 
 * The function should:
 * 1. Print the addresses
 * 2. Compare addresses to determine relative positions
 * 3. Explain which region each likely belongs to
 * 
 * HINT:
 * - Stack addresses are usually higher than heap
 * - Data segment addresses are usually lower than heap
 * - This is system-dependent!
 */

// Global variable for testing
static int globalVar = 100;

// YOUR CODE HERE
void analyzeMemory_exercise(void *stackPtr, void *heapPtr, void *dataPtr) {
    // TODO: Implement this function
}


/* =============================================================================
 * ANSWER KEY - REFERENCE SOLUTIONS
 * =============================================================================
 */

#ifdef SHOW_ANSWERS

/* EXERCISE 1 SOLUTION */
int* createInteger(int value) {
    int *ptr = (int*)malloc(sizeof(int));
    if (ptr != NULL) {
        *ptr = value;
    }
    return ptr;
}

/* EXERCISE 2 SOLUTION */
int* createArray(int size, int initValue) {
    if (size <= 0) return NULL;
    
    int *arr = (int*)malloc(size * sizeof(int));
    if (arr != NULL) {
        for (int i = 0; i < size; i++) {
            arr[i] = initValue;
        }
    }
    return arr;
}

/* EXERCISE 3 SOLUTION */
char* duplicateString(const char *src) {
    if (src == NULL) return NULL;
    
    size_t len = strlen(src) + 1;  // +1 for null terminator
    char *copy = (char*)malloc(len);
    if (copy != NULL) {
        strcpy(copy, src);
    }
    return copy;
}

/* EXERCISE 4 SOLUTION */
char* concatStrings(const char *s1, const char *s2) {
    if (s1 == NULL || s2 == NULL) return NULL;
    
    size_t len1 = strlen(s1);
    size_t len2 = strlen(s2);
    size_t totalLen = len1 + len2 + 1;  // +1 for null terminator
    
    char *result = (char*)malloc(totalLen);
    if (result != NULL) {
        strcpy(result, s1);
        strcat(result, s2);
    }
    return result;
}

/* EXERCISE 5 SOLUTION */
Person* createPerson(const char *name, int age, float height) {
    Person *p = (Person*)malloc(sizeof(Person));
    if (p != NULL) {
        // Safely copy name (max 49 chars + null)
        strncpy(p->name, name, 49);
        p->name[49] = '\0';  // Ensure null termination
        p->age = age;
        p->height = height;
    }
    return p;
}

/* EXERCISE 6 SOLUTION */
Person* createPeopleArray(int count) {
    if (count <= 0) return NULL;
    
    Person *people = (Person*)calloc(count, sizeof(Person));
    if (people != NULL) {
        for (int i = 0; i < count; i++) {
            strcpy(people[i].name, "Unknown");
            people[i].age = 0;
            people[i].height = 0.0f;
        }
    }
    return people;
}

/* EXERCISE 7 SOLUTION */
int safeRealloc(int **arr, int oldSize, int newSize, int initValue) {
    if (arr == NULL || *arr == NULL || newSize <= 0) {
        return 0;
    }
    
    int *temp = (int*)realloc(*arr, newSize * sizeof(int));
    if (temp == NULL) {
        return 0;  // Failed, original array unchanged
    }
    
    // Initialize new elements if growing
    if (newSize > oldSize) {
        for (int i = oldSize; i < newSize; i++) {
            temp[i] = initValue;
        }
    }
    
    *arr = temp;  // Update caller's pointer
    return 1;     // Success
}

/* EXERCISE 8 SOLUTION */
int** createMatrix(int rows, int cols, int initValue) {
    if (rows <= 0 || cols <= 0) return NULL;
    
    // Allocate row pointers
    int **matrix = (int**)malloc(rows * sizeof(int*));
    if (matrix == NULL) {
        return NULL;
    }
    
    // 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 NULL;
        }
        
        // Initialize row
        for (int j = 0; j < cols; j++) {
            matrix[i][j] = initValue;
        }
    }
    
    return matrix;
}

void freeMatrix(int **matrix, int rows) {
    if (matrix != NULL) {
        for (int i = 0; i < rows; i++) {
            free(matrix[i]);
        }
        free(matrix);
    }
}

/* EXERCISE 9 SOLUTION */
Stack* createStack(int capacity) {
    if (capacity <= 0) return NULL;
    
    Stack *s = (Stack*)malloc(sizeof(Stack));
    if (s == NULL) return NULL;
    
    s->data = (int*)malloc(capacity * sizeof(int));
    if (s->data == NULL) {
        free(s);
        return NULL;
    }
    
    s->top = -1;
    s->capacity = capacity;
    return s;
}

int push(Stack *s, int value) {
    if (s == NULL) return 0;
    
    // Check if full, need to grow
    if (s->top >= s->capacity - 1) {
        int newCapacity = s->capacity * 2;
        int *temp = (int*)realloc(s->data, newCapacity * sizeof(int));
        if (temp == NULL) {
            return 0;  // Failed to grow
        }
        s->data = temp;
        s->capacity = newCapacity;
    }
    
    s->data[++s->top] = value;
    return 1;
}

int pop(Stack *s, int *value) {
    if (s == NULL || s->top < 0) {
        return 0;  // Empty or invalid
    }
    
    *value = s->data[s->top--];
    return 1;
}

void freeStack(Stack *s) {
    if (s != NULL) {
        free(s->data);
        free(s);
    }
}

/* EXERCISE 10 SOLUTION */
void analyzeMemory(void *stackPtr, void *heapPtr, void *dataPtr) {
    printf("Memory Analysis:\n\n");
    
    printf("Addresses:\n");
    printf("  Stack pointer:  %p\n", stackPtr);
    printf("  Heap pointer:   %p\n", heapPtr);
    printf("  Data pointer:   %p\n", dataPtr);
    printf("\n");
    
    // Convert to unsigned long for comparison
    unsigned long stackAddr = (unsigned long)stackPtr;
    unsigned long heapAddr = (unsigned long)heapPtr;
    unsigned long dataAddr = (unsigned long)dataPtr;
    
    printf("Numerical values (for comparison):\n");
    printf("  Stack: %lu\n", stackAddr);
    printf("  Heap:  %lu\n", heapAddr);
    printf("  Data:  %lu\n", dataAddr);
    printf("\n");
    
    // Typical memory layout analysis
    printf("Typical Layout (varies by system):\n");
    printf("  High addresses: Stack\n");
    printf("  Middle:         Heap\n");
    printf("  Low addresses:  Data/Text segments\n");
    printf("\n");
    
    // Observed relationships
    printf("Observed relationships:\n");
    if (stackAddr > heapAddr) {
        printf("  Stack > Heap: Typical layout\n");
    } else {
        printf("  Heap > Stack: Unusual (or 32-bit addressing)\n");
    }
    
    if (heapAddr > dataAddr) {
        printf("  Heap > Data: Typical layout\n");
    } else {
        printf("  Data > Heap: Unusual\n");
    }
    
    printf("\nNote: Exact layout depends on OS, architecture, and compiler.\n");
}

#endif /* SHOW_ANSWERS */


/* =============================================================================
 * TEST PROGRAM
 * =============================================================================
 */

int main(void) {
    printf("============================================================\n");
    printf("   INTRODUCTION TO DYNAMIC MEMORY: EXERCISE TESTS\n");
    printf("============================================================\n\n");
    
    #ifdef SHOW_ANSWERS
    
    // Test Exercise 1
    printf("--- Exercise 1: Create Integer ---\n");
    int *p1 = createInteger(42);
    if (p1 != NULL) {
        printf("Created integer: %d\n", *p1);
        free(p1);
    }
    printf("\n");
    
    // Test Exercise 2
    printf("--- Exercise 2: Create Array ---\n");
    int *arr2 = createArray(5, 10);
    if (arr2 != NULL) {
        printf("Array: ");
        for (int i = 0; i < 5; i++) printf("%d ", arr2[i]);
        printf("\n");
        free(arr2);
    }
    printf("\n");
    
    // Test Exercise 3
    printf("--- Exercise 3: Duplicate String ---\n");
    char *str3 = duplicateString("Hello, World!");
    if (str3 != NULL) {
        printf("Duplicated: \"%s\"\n", str3);
        free(str3);
    }
    printf("\n");
    
    // Test Exercise 4
    printf("--- Exercise 4: Concatenate Strings ---\n");
    char *str4 = concatStrings("Hello, ", "World!");
    if (str4 != NULL) {
        printf("Concatenated: \"%s\"\n", str4);
        free(str4);
    }
    printf("\n");
    
    // Test Exercise 5
    printf("--- Exercise 5: Create Person ---\n");
    Person *p5 = createPerson("Alice", 25, 5.6f);
    if (p5 != NULL) {
        printf("Person: %s, %d years, %.1f ft\n", p5->name, p5->age, p5->height);
        free(p5);
    }
    printf("\n");
    
    // Test Exercise 6
    printf("--- Exercise 6: People Array ---\n");
    Person *people6 = createPeopleArray(3);
    if (people6 != NULL) {
        for (int i = 0; i < 3; i++) {
            printf("Person %d: %s\n", i, people6[i].name);
        }
        free(people6);
    }
    printf("\n");
    
    // Test Exercise 7
    printf("--- Exercise 7: Safe Realloc ---\n");
    int *arr7 = createArray(3, 5);
    if (arr7 != NULL) {
        printf("Before: ");
        for (int i = 0; i < 3; i++) printf("%d ", arr7[i]);
        printf("\n");
        
        if (safeRealloc(&arr7, 3, 6, 0)) {
            printf("After realloc: ");
            for (int i = 0; i < 6; i++) printf("%d ", arr7[i]);
            printf("\n");
        }
        free(arr7);
    }
    printf("\n");
    
    // Test Exercise 8
    printf("--- Exercise 8: Create Matrix ---\n");
    int **mat8 = createMatrix(3, 4, 1);
    if (mat8 != NULL) {
        printf("3x4 Matrix (all 1s):\n");
        for (int i = 0; i < 3; i++) {
            printf("  ");
            for (int j = 0; j < 4; j++) printf("%d ", mat8[i][j]);
            printf("\n");
        }
        freeMatrix(mat8, 3);
    }
    printf("\n");
    
    // Test Exercise 9
    printf("--- Exercise 9: Dynamic Stack ---\n");
    Stack *s9 = createStack(2);
    if (s9 != NULL) {
        push(s9, 10);
        push(s9, 20);
        push(s9, 30);  // Should trigger realloc
        push(s9, 40);
        
        int val;
        printf("Popping: ");
        while (pop(s9, &val)) {
            printf("%d ", val);
        }
        printf("\n");
        
        freeStack(s9);
    }
    printf("\n");
    
    // Test Exercise 10
    printf("--- Exercise 10: Memory Analysis ---\n");
    int stackVar = 100;
    int *heapVar = malloc(sizeof(int));
    analyzeMemory(&stackVar, heapVar, &globalVar);
    free(heapVar);
    printf("\n");
    
    #else
    printf("Compile with -DSHOW_ANSWERS to see solution tests.\n");
    printf("Example: gcc exercises.c -o exercises -DSHOW_ANSWERS\n\n");
    
    printf("Exercises to complete:\n");
    printf("  1. createInteger - Allocate and initialize single integer\n");
    printf("  2. createArray - Allocate and initialize integer array\n");
    printf("  3. duplicateString - Create copy of a string\n");
    printf("  4. concatStrings - Concatenate two strings\n");
    printf("  5. createPerson - Allocate and fill structure\n");
    printf("  6. createPeopleArray - Array of structures\n");
    printf("  7. safeRealloc - Resize array safely\n");
    printf("  8. createMatrix/freeMatrix - 2D array\n");
    printf("  9. Stack operations - Dynamic stack\n");
    printf(" 10. analyzeMemory - Memory layout analysis\n");
    #endif
    
    printf("============================================================\n");
    printf("   EXERCISES COMPLETE\n");
    printf("============================================================\n");
    
    return 0;
}
Exercises - C Programming Tutorial | DeepML