c
examples
examples.c🔧c
/**
* =============================================================================
* INTRODUCTION TO DYNAMIC MEMORY ALLOCATION: EXAMPLES
* =============================================================================
*
* This file demonstrates fundamental concepts of dynamic memory allocation in C.
*
* Topics covered:
* - Static vs dynamic memory comparison
* - Memory layout visualization
* - Basic malloc, calloc, realloc, free usage
* - Heap behavior demonstration
* - Common patterns and idioms
* - Error handling for allocations
*
* Compile: gcc examples.c -o examples
* Run: ./examples
* =============================================================================
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
/**
* =============================================================================
* EXAMPLE 1: Static vs Dynamic Memory
* =============================================================================
* Demonstrates the difference between stack and heap allocation.
*/
void example1_static_vs_dynamic(void) {
printf("=== EXAMPLE 1: Static vs Dynamic Memory ===\n\n");
// -------------------------------------------------------------------------
// Static allocation (stack)
// -------------------------------------------------------------------------
printf("--- Static Allocation (Stack) ---\n");
int staticArray[5] = {1, 2, 3, 4, 5}; // Size must be known at compile time
printf("Static array: ");
for (int i = 0; i < 5; i++) {
printf("%d ", staticArray[i]);
}
printf("\n");
printf("Address: %p (on stack)\n", (void*)staticArray);
printf("Size: 5 elements (fixed at compile time)\n\n");
// -------------------------------------------------------------------------
// Dynamic allocation (heap)
// -------------------------------------------------------------------------
printf("--- Dynamic Allocation (Heap) ---\n");
int size = 5; // Size can be determined at runtime
int *dynamicArray = (int*)malloc(size * sizeof(int));
if (dynamicArray == NULL) {
printf("Memory allocation failed!\n");
return;
}
// Initialize the dynamic array
for (int i = 0; i < size; i++) {
dynamicArray[i] = (i + 1) * 10;
}
printf("Dynamic array: ");
for (int i = 0; i < size; i++) {
printf("%d ", dynamicArray[i]);
}
printf("\n");
printf("Address: %p (on heap)\n", (void*)dynamicArray);
printf("Size: %d elements (determined at runtime)\n", size);
// Free dynamically allocated memory
free(dynamicArray);
dynamicArray = NULL; // Good practice
printf("\nKey Difference:\n");
printf(" Static: Size fixed at compile time, automatic cleanup\n");
printf(" Dynamic: Size flexible at runtime, manual cleanup required\n");
printf("\n");
}
/**
* =============================================================================
* EXAMPLE 2: Memory Layout Visualization
* =============================================================================
* Shows where different types of variables are stored in memory.
*/
// Global variables (data segment)
int globalInitialized = 100; // Initialized data segment
int globalUninitialized; // BSS segment (zero-initialized)
void example2_memory_layout(void) {
printf("=== EXAMPLE 2: Memory Layout Visualization ===\n\n");
// Static variable (data segment)
static int staticVar = 200;
// Local variable (stack)
int localVar = 300;
// Dynamic memory (heap)
int *heapVar = (int*)malloc(sizeof(int));
*heapVar = 400;
// Function pointer (to show code segment)
void (*funcPtr)(void) = example2_memory_layout;
printf("Memory Regions (approximate locations):\n\n");
printf("Code Segment (Low Address):\n");
printf(" Function address: %p\n", (void*)funcPtr);
printf("\n");
printf("Data Segment (Initialized):\n");
printf(" globalInitialized: %p, value = %d\n",
(void*)&globalInitialized, globalInitialized);
printf(" staticVar: %p, value = %d\n",
(void*)&staticVar, staticVar);
printf("\n");
printf("BSS Segment (Uninitialized):\n");
printf(" globalUninitialized: %p, value = %d\n",
(void*)&globalUninitialized, globalUninitialized);
printf("\n");
printf("Heap (Dynamic):\n");
printf(" heapVar points to: %p, value = %d\n",
(void*)heapVar, *heapVar);
printf("\n");
printf("Stack (Local):\n");
printf(" localVar: %p, value = %d\n",
(void*)&localVar, localVar);
printf(" heapVar: %p (pointer itself on stack)\n",
(void*)&heapVar);
printf("\n");
printf("Typical Memory Layout (Low to High):\n");
printf(" +------------------+\n");
printf(" | Text (Code) | <- Lowest\n");
printf(" +------------------+\n");
printf(" | Data (Init) |\n");
printf(" +------------------+\n");
printf(" | BSS (Uninit) |\n");
printf(" +------------------+\n");
printf(" | Heap ↓ | <- Grows upward\n");
printf(" | ... |\n");
printf(" | Stack ↑ | <- Grows downward\n");
printf(" +------------------+ <- Highest\n");
printf("\n");
free(heapVar);
}
/**
* =============================================================================
* EXAMPLE 3: Basic malloc() Usage
* =============================================================================
* Demonstrates various ways to use malloc().
*/
void example3_malloc_basics(void) {
printf("=== EXAMPLE 3: Basic malloc() Usage ===\n\n");
// -------------------------------------------------------------------------
// Allocating a single integer
// -------------------------------------------------------------------------
printf("--- Allocating Single Integer ---\n");
int *singleInt = (int*)malloc(sizeof(int));
if (singleInt == NULL) {
printf("Allocation failed!\n");
return;
}
*singleInt = 42;
printf("Allocated integer: %d at %p\n", *singleInt, (void*)singleInt);
printf("Size requested: %zu bytes\n", sizeof(int));
free(singleInt);
printf("Memory freed.\n\n");
// -------------------------------------------------------------------------
// Allocating an array
// -------------------------------------------------------------------------
printf("--- Allocating Integer Array ---\n");
int n = 10;
int *array = (int*)malloc(n * sizeof(int));
if (array == NULL) {
printf("Allocation failed!\n");
return;
}
printf("Allocated array of %d integers at %p\n", n, (void*)array);
printf("Total size: %zu bytes\n", n * sizeof(int));
// Initialize with values
for (int i = 0; i < n; i++) {
array[i] = i * i; // Squares
}
printf("Array values: ");
for (int i = 0; i < n; i++) {
printf("%d ", array[i]);
}
printf("\n");
free(array);
printf("Array freed.\n\n");
// -------------------------------------------------------------------------
// Allocating a string
// -------------------------------------------------------------------------
printf("--- Allocating String ---\n");
const char *original = "Hello, Dynamic World!";
size_t len = strlen(original) + 1; // +1 for null terminator
char *str = (char*)malloc(len);
if (str == NULL) {
printf("Allocation failed!\n");
return;
}
strcpy(str, original);
printf("Allocated string: \"%s\"\n", str);
printf("String length: %zu (including null terminator)\n", len);
printf("Address: %p\n", (void*)str);
free(str);
printf("String freed.\n\n");
// -------------------------------------------------------------------------
// Best practice: sizeof with variable
// -------------------------------------------------------------------------
printf("--- Best Practice: sizeof(*pointer) ---\n");
double *d = malloc(sizeof(*d)); // Type only in declaration
if (d != NULL) {
*d = 3.14159;
printf("Using sizeof(*d): Allocated %zu bytes for double\n", sizeof(*d));
printf("Value: %f\n", *d);
free(d);
}
printf("\nWhy sizeof(*pointer) is better:\n");
printf(" - Type appears only once (in declaration)\n");
printf(" - Changing type requires only one edit\n");
printf(" - Less error-prone\n");
printf("\n");
}
/**
* =============================================================================
* EXAMPLE 4: malloc() Returns Uninitialized Memory
* =============================================================================
* Shows that malloc() does NOT initialize memory to zero.
*/
void example4_uninitialized_memory(void) {
printf("=== EXAMPLE 4: malloc() Returns Uninitialized Memory ===\n\n");
printf("malloc() does NOT initialize memory!\n");
printf("The memory contains whatever was there before (garbage).\n\n");
// Allocate memory without initializing
int *arr = (int*)malloc(10 * sizeof(int));
if (arr == NULL) {
printf("Allocation failed!\n");
return;
}
printf("Contents of uninitialized array (garbage values):\n");
for (int i = 0; i < 10; i++) {
printf(" arr[%d] = %d\n", i, arr[i]);
}
printf("\n");
printf("This is why you should always initialize after malloc!\n\n");
// Proper initialization
printf("After initialization:\n");
for (int i = 0; i < 10; i++) {
arr[i] = 0; // Or use memset
}
for (int i = 0; i < 10; i++) {
printf(" arr[%d] = %d\n", i, arr[i]);
}
free(arr);
printf("\n");
// Alternative: use memset
printf("Alternative: Using memset() for initialization:\n");
int *arr2 = (int*)malloc(5 * sizeof(int));
if (arr2 != NULL) {
memset(arr2, 0, 5 * sizeof(int)); // Set all bytes to 0
printf("After memset: ");
for (int i = 0; i < 5; i++) {
printf("%d ", arr2[i]);
}
printf("\n");
free(arr2);
}
printf("\n");
}
/**
* =============================================================================
* EXAMPLE 5: calloc() - Zero-Initialized Memory
* =============================================================================
* Demonstrates calloc() which initializes memory to zero.
*/
void example5_calloc_usage(void) {
printf("=== EXAMPLE 5: calloc() - Zero-Initialized Memory ===\n\n");
printf("calloc(n, size) allocates n elements of 'size' bytes each,\n");
printf("and initializes ALL bits to zero.\n\n");
// -------------------------------------------------------------------------
// Basic calloc usage
// -------------------------------------------------------------------------
printf("--- Basic calloc Usage ---\n");
int *arr = (int*)calloc(10, sizeof(int));
if (arr == NULL) {
printf("Allocation failed!\n");
return;
}
printf("calloc(10, sizeof(int)) result:\n");
for (int i = 0; i < 10; i++) {
printf(" arr[%d] = %d\n", i, arr[i]);
}
printf("\nAll values are zero - no explicit initialization needed!\n\n");
free(arr);
// -------------------------------------------------------------------------
// calloc vs malloc comparison
// -------------------------------------------------------------------------
printf("--- calloc vs malloc Comparison ---\n\n");
printf("malloc(n * sizeof(int)):\n");
printf(" - Faster (no initialization)\n");
printf(" - Memory contains garbage\n");
printf(" - Must initialize manually if zeros needed\n\n");
printf("calloc(n, sizeof(int)):\n");
printf(" - Slightly slower (zero initialization)\n");
printf(" - Memory guaranteed to be zero\n");
printf(" - Overflow protection: n * size computed safely\n\n");
// -------------------------------------------------------------------------
// Calloc for structures
// -------------------------------------------------------------------------
printf("--- calloc for Structures ---\n");
typedef struct {
int id;
char name[50];
float score;
} Student;
Student *students = (Student*)calloc(3, sizeof(Student));
if (students != NULL) {
printf("Allocated 3 Student structures with calloc:\n");
for (int i = 0; i < 3; i++) {
printf(" Student %d: id=%d, name=\"%s\", score=%.2f\n",
i, students[i].id, students[i].name, students[i].score);
}
printf("\nAll fields are zero/empty - safe to use immediately!\n");
free(students);
}
printf("\n");
}
/**
* =============================================================================
* EXAMPLE 6: realloc() - Resizing Memory
* =============================================================================
* Demonstrates how to resize dynamically allocated memory.
*/
void example6_realloc_usage(void) {
printf("=== EXAMPLE 6: realloc() - Resizing Memory ===\n\n");
// -------------------------------------------------------------------------
// Growing an array
// -------------------------------------------------------------------------
printf("--- Growing an Array ---\n");
int capacity = 5;
int *arr = (int*)malloc(capacity * sizeof(int));
if (arr == NULL) {
printf("Initial allocation failed!\n");
return;
}
// Fill initial array
for (int i = 0; i < capacity; i++) {
arr[i] = i + 1;
}
printf("Initial array (capacity %d): ", capacity);
for (int i = 0; i < capacity; i++) {
printf("%d ", arr[i]);
}
printf("\nAddress: %p\n\n", (void*)arr);
// Need more space - double the capacity
int newCapacity = capacity * 2;
int *temp = (int*)realloc(arr, newCapacity * sizeof(int));
if (temp == NULL) {
printf("Reallocation failed! Original array preserved.\n");
free(arr);
return;
}
arr = temp; // Update pointer (might have moved)
// Add more elements
for (int i = capacity; i < newCapacity; i++) {
arr[i] = i + 1;
}
printf("After realloc (capacity %d): ", newCapacity);
for (int i = 0; i < newCapacity; i++) {
printf("%d ", arr[i]);
}
printf("\nAddress: %p ", (void*)arr);
printf("(may have changed)\n\n");
capacity = newCapacity;
// -------------------------------------------------------------------------
// Shrinking an array
// -------------------------------------------------------------------------
printf("--- Shrinking an Array ---\n");
newCapacity = 3;
temp = (int*)realloc(arr, newCapacity * sizeof(int));
if (temp != NULL) {
arr = temp;
printf("Shrunk to capacity %d: ", newCapacity);
for (int i = 0; i < newCapacity; i++) {
printf("%d ", arr[i]);
}
printf("\nData beyond new size is lost!\n\n");
}
free(arr);
// -------------------------------------------------------------------------
// realloc special cases
// -------------------------------------------------------------------------
printf("--- realloc Special Cases ---\n\n");
printf("1. realloc(NULL, size) = malloc(size)\n");
int *p1 = realloc(NULL, sizeof(int)); // Same as malloc
if (p1 != NULL) {
*p1 = 100;
printf(" Created: %d\n", *p1);
free(p1);
}
printf("\n2. realloc(ptr, 0) = free(ptr) (implementation-defined)\n");
printf(" Note: Behavior varies; some return NULL, some non-NULL\n");
printf(" Best practice: use free() explicitly instead\n");
printf("\n3. If realloc fails, original memory is unchanged\n");
printf(" Always use a temp pointer to check success!\n");
printf("\n");
}
/**
* =============================================================================
* EXAMPLE 7: Proper Error Handling
* =============================================================================
* Demonstrates how to properly handle allocation failures.
*/
void example7_error_handling(void) {
printf("=== EXAMPLE 7: Proper Error Handling ===\n\n");
// -------------------------------------------------------------------------
// Basic NULL check
// -------------------------------------------------------------------------
printf("--- Basic NULL Check ---\n");
int *ptr = (int*)malloc(100 * sizeof(int));
if (ptr == NULL) {
fprintf(stderr, "Error: Failed to allocate memory\n");
// Handle error: exit, return error code, etc.
} else {
printf("Allocation successful: %p\n", (void*)ptr);
free(ptr);
}
printf("\n");
// -------------------------------------------------------------------------
// Safe wrapper function
// -------------------------------------------------------------------------
printf("--- Safe Wrapper Function ---\n");
printf("Code pattern:\n\n");
printf("void* safe_malloc(size_t size) {\n");
printf(" void *ptr = malloc(size);\n");
printf(" if (ptr == NULL) {\n");
printf(" fprintf(stderr, \"Fatal: Out of memory\\n\");\n");
printf(" exit(EXIT_FAILURE);\n");
printf(" }\n");
printf(" return ptr;\n");
printf("}\n\n");
// -------------------------------------------------------------------------
// Handling realloc failure correctly
// -------------------------------------------------------------------------
printf("--- Correct realloc Pattern ---\n\n");
printf("WRONG way:\n");
printf(" ptr = realloc(ptr, newSize); // Memory leak if fails!\n\n");
printf("CORRECT way:\n");
printf(" int *temp = realloc(ptr, newSize);\n");
printf(" if (temp == NULL) {\n");
printf(" // Handle error, ptr still valid\n");
printf(" free(ptr); // Or continue using ptr\n");
printf(" } else {\n");
printf(" ptr = temp; // Update pointer\n");
printf(" }\n\n");
// Demonstrate
int *arr = malloc(5 * sizeof(int));
if (arr != NULL) {
for (int i = 0; i < 5; i++) arr[i] = i;
int *temp = realloc(arr, 10 * sizeof(int));
if (temp == NULL) {
printf("Realloc failed, but original 'arr' still valid\n");
// Could continue using arr, or free it
} else {
arr = temp;
printf("Realloc succeeded, pointer updated\n");
}
free(arr);
}
printf("\n");
}
/**
* =============================================================================
* EXAMPLE 8: Memory Leaks Demonstration
* =============================================================================
* Shows how memory leaks occur (educational - intentional leaks).
*/
void example8_memory_leaks(void) {
printf("=== EXAMPLE 8: Memory Leaks Demonstration ===\n\n");
printf("A memory leak occurs when allocated memory is never freed.\n");
printf("Below are common causes (illustrated, but we clean up).\n\n");
// -------------------------------------------------------------------------
// Leak Type 1: Lost pointer
// -------------------------------------------------------------------------
printf("--- Leak Type 1: Lost Pointer ---\n");
printf("Code:\n");
printf(" void leaky() {\n");
printf(" int *p = malloc(100);\n");
printf(" // p goes out of scope without free()\n");
printf(" } // Memory leaked!\n\n");
// -------------------------------------------------------------------------
// Leak Type 2: Overwritten pointer
// -------------------------------------------------------------------------
printf("--- Leak Type 2: Overwritten Pointer ---\n");
printf("Code:\n");
printf(" int *p = malloc(100);\n");
printf(" p = malloc(200); // First allocation leaked!\n\n");
// Correct version:
int *p = malloc(100);
if (p != NULL) {
free(p); // Free first!
p = malloc(200); // Then reallocate
if (p != NULL) {
free(p);
}
}
printf("Correct: free() before reassigning\n\n");
// -------------------------------------------------------------------------
// Leak Type 3: Early return
// -------------------------------------------------------------------------
printf("--- Leak Type 3: Early Return ---\n");
printf("Code:\n");
printf(" int *p = malloc(100);\n");
printf(" if (error) {\n");
printf(" return; // p leaked!\n");
printf(" }\n");
printf(" free(p);\n\n");
printf("Correct: Always have cleanup path\n");
printf(" int *p = malloc(100);\n");
printf(" if (error) {\n");
printf(" free(p); // Clean up first!\n");
printf(" return;\n");
printf(" }\n");
printf(" free(p);\n\n");
// -------------------------------------------------------------------------
// Detection tools
// -------------------------------------------------------------------------
printf("--- Memory Leak Detection Tools ---\n\n");
printf("1. Valgrind (Linux): valgrind --leak-check=full ./program\n");
printf("2. AddressSanitizer: gcc -fsanitize=address program.c\n");
printf("3. Dr. Memory (Windows)\n");
printf("4. LeakSanitizer: gcc -fsanitize=leak program.c\n");
printf("\n");
}
/**
* =============================================================================
* EXAMPLE 9: Dangling Pointers
* =============================================================================
* Demonstrates the danger of using freed memory.
*/
void example9_dangling_pointers(void) {
printf("=== EXAMPLE 9: Dangling Pointers ===\n\n");
printf("A dangling pointer points to memory that has been freed.\n");
printf("Using it causes UNDEFINED BEHAVIOR.\n\n");
// -------------------------------------------------------------------------
// Creating a dangling pointer
// -------------------------------------------------------------------------
printf("--- How Dangling Pointers Occur ---\n\n");
int *ptr = (int*)malloc(sizeof(int));
*ptr = 42;
printf("Before free: ptr = %p, *ptr = %d\n", (void*)ptr, *ptr);
free(ptr);
// ptr is now dangling - it still holds the address, but memory is freed
printf("After free: ptr = %p (still has address!)\n", (void*)ptr);
printf(" But memory at that address may be:\n");
printf(" - Reused by another allocation\n");
printf(" - Marked as inaccessible\n");
printf(" - Contain garbage\n\n");
// -------------------------------------------------------------------------
// Prevention: Set to NULL
// -------------------------------------------------------------------------
printf("--- Prevention: Set to NULL ---\n\n");
ptr = NULL; // IMPORTANT: Prevents accidental use
printf("After ptr = NULL:\n");
printf(" ptr = %p\n", (void*)ptr);
printf(" Accessing *ptr would cause segfault (detectable crash)\n");
printf(" Can check: if (ptr != NULL) { ... }\n\n");
// -------------------------------------------------------------------------
// Double free danger
// -------------------------------------------------------------------------
printf("--- Double Free Danger ---\n\n");
printf("WRONG:\n");
printf(" free(ptr);\n");
printf(" free(ptr); // UNDEFINED BEHAVIOR!\n\n");
printf("CORRECT:\n");
printf(" free(ptr);\n");
printf(" ptr = NULL;\n");
printf(" free(ptr); // Safe: free(NULL) does nothing\n\n");
// Safe demonstration
int *p2 = malloc(sizeof(int));
free(p2);
p2 = NULL;
free(p2); // This is safe
printf("free(NULL) is always safe.\n");
printf("\n");
}
/**
* =============================================================================
* EXAMPLE 10: Allocating Structures
* =============================================================================
* Shows how to dynamically allocate structures.
*/
void example10_allocating_structures(void) {
printf("=== EXAMPLE 10: Allocating Structures ===\n\n");
// -------------------------------------------------------------------------
// Define a structure
// -------------------------------------------------------------------------
typedef struct {
int id;
char name[50];
float gpa;
} Student;
// -------------------------------------------------------------------------
// Single structure allocation
// -------------------------------------------------------------------------
printf("--- Single Structure Allocation ---\n");
Student *s1 = (Student*)malloc(sizeof(Student));
if (s1 == NULL) {
printf("Allocation failed!\n");
return;
}
// Initialize
s1->id = 101;
strcpy(s1->name, "Alice Johnson");
s1->gpa = 3.85;
printf("Student: ID=%d, Name=%s, GPA=%.2f\n",
s1->id, s1->name, s1->gpa);
printf("Size: %zu bytes\n", sizeof(Student));
printf("Address: %p\n\n", (void*)s1);
free(s1);
// -------------------------------------------------------------------------
// Array of structures
// -------------------------------------------------------------------------
printf("--- Array of Structures ---\n");
int numStudents = 3;
Student *students = (Student*)calloc(numStudents, sizeof(Student));
if (students == NULL) {
printf("Allocation failed!\n");
return;
}
// Initialize array
students[0] = (Student){101, "Alice", 3.85};
students[1] = (Student){102, "Bob", 3.50};
students[2] = (Student){103, "Charlie", 3.92};
printf("Student array:\n");
for (int i = 0; i < numStudents; i++) {
printf(" [%d] ID=%d, Name=%s, GPA=%.2f\n",
i, students[i].id, students[i].name, students[i].gpa);
}
printf("Total size: %zu bytes\n\n", numStudents * sizeof(Student));
free(students);
// -------------------------------------------------------------------------
// Structure with pointer members
// -------------------------------------------------------------------------
printf("--- Structure with Dynamic Members ---\n");
typedef struct {
int id;
char *name; // Dynamic string
int *scores; // Dynamic array
int numScores;
} DynamicStudent;
DynamicStudent *ds = malloc(sizeof(DynamicStudent));
if (ds != NULL) {
ds->id = 201;
// Allocate name
ds->name = malloc(50);
if (ds->name != NULL) {
strcpy(ds->name, "Dynamic Dave");
}
// Allocate scores array
ds->numScores = 5;
ds->scores = malloc(ds->numScores * sizeof(int));
if (ds->scores != NULL) {
int scores[] = {85, 90, 78, 92, 88};
for (int i = 0; i < ds->numScores; i++) {
ds->scores[i] = scores[i];
}
}
printf("Dynamic Student:\n");
printf(" ID: %d\n", ds->id);
printf(" Name: %s\n", ds->name);
printf(" Scores: ");
for (int i = 0; i < ds->numScores; i++) {
printf("%d ", ds->scores[i]);
}
printf("\n\n");
printf("Remember: Free members BEFORE freeing structure!\n");
// Free in reverse order
free(ds->scores);
free(ds->name);
free(ds);
}
printf("\n");
}
/**
* =============================================================================
* EXAMPLE 11: 2D Array Allocation
* =============================================================================
* Demonstrates different ways to allocate 2D arrays dynamically.
*/
void example11_2d_array_allocation(void) {
printf("=== EXAMPLE 11: 2D Array Allocation ===\n\n");
int rows = 3, cols = 4;
// -------------------------------------------------------------------------
// Method 1: Array of pointers
// -------------------------------------------------------------------------
printf("--- Method 1: Array of Pointers ---\n");
int **matrix1 = (int**)malloc(rows * sizeof(int*));
if (matrix1 == NULL) {
printf("Allocation failed!\n");
return;
}
for (int i = 0; i < rows; i++) {
matrix1[i] = (int*)malloc(cols * sizeof(int));
if (matrix1[i] == NULL) {
// Cleanup previously allocated rows
for (int j = 0; j < i; j++) {
free(matrix1[j]);
}
free(matrix1);
printf("Allocation failed!\n");
return;
}
}
// Initialize
int val = 1;
for (int i = 0; i < rows; i++) {
for (int j = 0; j < cols; j++) {
matrix1[i][j] = val++;
}
}
printf("Matrix 1 (array of pointers):\n");
for (int i = 0; i < rows; i++) {
printf(" ");
for (int j = 0; j < cols; j++) {
printf("%3d ", matrix1[i][j]);
}
printf("\n");
}
printf("\n");
// Free
for (int i = 0; i < rows; i++) {
free(matrix1[i]);
}
free(matrix1);
// -------------------------------------------------------------------------
// Method 2: Single contiguous block
// -------------------------------------------------------------------------
printf("--- Method 2: Single Contiguous Block ---\n");
int *matrix2 = (int*)malloc(rows * cols * sizeof(int));
if (matrix2 == NULL) {
printf("Allocation failed!\n");
return;
}
// Access using index calculation: matrix2[i * cols + j]
val = 1;
for (int i = 0; i < rows; i++) {
for (int j = 0; j < cols; j++) {
matrix2[i * cols + j] = val++;
}
}
printf("Matrix 2 (contiguous block):\n");
for (int i = 0; i < rows; i++) {
printf(" ");
for (int j = 0; j < cols; j++) {
printf("%3d ", matrix2[i * cols + j]);
}
printf("\n");
}
printf("\n");
// Single free
free(matrix2);
printf("Comparison:\n");
printf(" Method 1: Flexible rows, multiple allocations\n");
printf(" Method 2: Cache-friendly, single allocation\n");
printf("\n");
}
/**
* =============================================================================
* EXAMPLE 12: Common Idioms and Patterns
* =============================================================================
* Shows common patterns for dynamic memory usage.
*/
void example12_common_patterns(void) {
printf("=== EXAMPLE 12: Common Idioms and Patterns ===\n\n");
// -------------------------------------------------------------------------
// Pattern 1: Factory function
// -------------------------------------------------------------------------
printf("--- Pattern 1: Factory Function ---\n");
printf("char* createString(const char *src) {\n");
printf(" char *copy = malloc(strlen(src) + 1);\n");
printf(" if (copy != NULL) {\n");
printf(" strcpy(copy, src);\n");
printf(" }\n");
printf(" return copy; // Caller must free\n");
printf("}\n\n");
// -------------------------------------------------------------------------
// Pattern 2: Growable buffer
// -------------------------------------------------------------------------
printf("--- Pattern 2: Growable Buffer ---\n");
int capacity = 4;
int size = 0;
int *buffer = malloc(capacity * sizeof(int));
if (buffer != NULL) {
// Add elements, growing when needed
for (int i = 1; i <= 10; i++) {
if (size >= capacity) {
capacity *= 2;
int *temp = realloc(buffer, capacity * sizeof(int));
if (temp == NULL) {
printf("Resize failed!\n");
break;
}
buffer = temp;
printf("Grew buffer to capacity %d\n", capacity);
}
buffer[size++] = i * 10;
}
printf("Buffer contents: ");
for (int i = 0; i < size; i++) {
printf("%d ", buffer[i]);
}
printf("\n\n");
free(buffer);
}
// -------------------------------------------------------------------------
// Pattern 3: Cleanup on error (goto cleanup)
// -------------------------------------------------------------------------
printf("--- Pattern 3: Cleanup on Error ---\n");
printf("int process() {\n");
printf(" int *a = malloc(100);\n");
printf(" if (!a) goto cleanup;\n");
printf(" int *b = malloc(200);\n");
printf(" if (!b) goto cleanup;\n");
printf(" // ... use a and b ...\n");
printf(" free(b);\n");
printf(" free(a);\n");
printf(" return 0;\n");
printf("cleanup:\n");
printf(" free(b); // free(NULL) is safe\n");
printf(" free(a);\n");
printf(" return -1;\n");
printf("}\n\n");
// -------------------------------------------------------------------------
// Pattern 4: Output parameter
// -------------------------------------------------------------------------
printf("--- Pattern 4: Output Parameter ---\n");
printf("int readData(int **data, int *size) {\n");
printf(" *data = malloc(100 * sizeof(int));\n");
printf(" if (*data == NULL) return -1;\n");
printf(" *size = 100;\n");
printf(" return 0; // Success\n");
printf("}\n");
printf("// Caller: int *data; int size;\n");
printf("// readData(&data, &size);\n");
printf("\n");
}
/**
* =============================================================================
* MAIN FUNCTION
* =============================================================================
*/
int main(void) {
printf("****************************************************************\n");
printf("* INTRODUCTION TO DYNAMIC MEMORY ALLOCATION - EXAMPLES *\n");
printf("****************************************************************\n\n");
example1_static_vs_dynamic();
example2_memory_layout();
example3_malloc_basics();
example4_uninitialized_memory();
example5_calloc_usage();
example6_realloc_usage();
example7_error_handling();
example8_memory_leaks();
example9_dangling_pointers();
example10_allocating_structures();
example11_2d_array_allocation();
example12_common_patterns();
printf("****************************************************************\n");
printf("* EXAMPLES COMPLETE *\n");
printf("****************************************************************\n");
return 0;
}