c
exercises
exercises.c🔧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;
}