c
examples
examples.c🔧c
/**
* =============================================================================
* MALLOC() AND FREE() - EXAMPLES
* =============================================================================
*
* This file demonstrates practical usage of malloc() and free() in C.
*
* Topics covered:
* - Basic malloc and free operations
* - Size calculations with sizeof
* - Allocating different data types
* - Common patterns and idioms
* - Error handling
* - Memory management best practices
*
* Compile: gcc examples.c -o examples
* Run: ./examples
* =============================================================================
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
/**
* =============================================================================
* EXAMPLE 1: Basic malloc and free
* =============================================================================
* Shows the simplest use of malloc and free.
*/
void example1_basic_malloc_free(void) {
printf("=== EXAMPLE 1: Basic malloc and free ===\n\n");
// -------------------------------------------------------------------------
// Allocating a single integer
// -------------------------------------------------------------------------
printf("--- Allocating Single Integer ---\n");
// Method 1: With explicit cast
int *p1 = (int*)malloc(sizeof(int));
// Method 2: Without cast (valid in C)
int *p2 = malloc(sizeof(int));
// Method 3: Using sizeof on the variable (RECOMMENDED)
int *p3 = malloc(sizeof(*p3));
// Always check for NULL
if (p1 == NULL || p2 == NULL || p3 == NULL) {
printf("Memory allocation failed!\n");
// Clean up any successful allocations
free(p1);
free(p2);
free(p3);
return;
}
// Use the allocated memory
*p1 = 100;
*p2 = 200;
*p3 = 300;
printf("p1 = %d (at %p)\n", *p1, (void*)p1);
printf("p2 = %d (at %p)\n", *p2, (void*)p2);
printf("p3 = %d (at %p)\n", *p3, (void*)p3);
printf("\n");
// Free the memory
free(p1);
free(p2);
free(p3);
// Set to NULL (good practice)
p1 = p2 = p3 = NULL;
printf("Memory freed and pointers set to NULL\n\n");
}
/**
* =============================================================================
* EXAMPLE 2: sizeof Operator with malloc
* =============================================================================
* Demonstrates correct use of sizeof for portable code.
*/
void example2_sizeof_usage(void) {
printf("=== EXAMPLE 2: sizeof Operator with malloc ===\n\n");
// -------------------------------------------------------------------------
// Size of fundamental types
// -------------------------------------------------------------------------
printf("--- Sizes of Fundamental Types ---\n");
printf("sizeof(char) = %zu bytes\n", sizeof(char));
printf("sizeof(short) = %zu bytes\n", sizeof(short));
printf("sizeof(int) = %zu bytes\n", sizeof(int));
printf("sizeof(long) = %zu bytes\n", sizeof(long));
printf("sizeof(long long) = %zu bytes\n", sizeof(long long));
printf("sizeof(float) = %zu bytes\n", sizeof(float));
printf("sizeof(double) = %zu bytes\n", sizeof(double));
printf("sizeof(void*) = %zu bytes\n", sizeof(void*));
printf("\n");
// -------------------------------------------------------------------------
// Correct sizeof usage with malloc
// -------------------------------------------------------------------------
printf("--- Correct sizeof Usage ---\n");
// Allocating different types
char *c = malloc(sizeof(*c)); // 1 byte
short *s = malloc(sizeof(*s)); // 2 bytes typically
int *i = malloc(sizeof(*i)); // 4 bytes typically
double *d = malloc(sizeof(*d)); // 8 bytes typically
if (c && s && i && d) {
printf("Allocated char at %p (%zu bytes)\n", (void*)c, sizeof(*c));
printf("Allocated short at %p (%zu bytes)\n", (void*)s, sizeof(*s));
printf("Allocated int at %p (%zu bytes)\n", (void*)i, sizeof(*i));
printf("Allocated double at %p (%zu bytes)\n", (void*)d, sizeof(*d));
}
free(c);
free(s);
free(i);
free(d);
printf("\n");
// -------------------------------------------------------------------------
// Why sizeof(*ptr) is better
// -------------------------------------------------------------------------
printf("--- Why sizeof(*ptr) is Better ---\n");
// If you change the type, only ONE place to update:
long *value = malloc(sizeof(*value));
// Change 'long' to 'double' above, sizeof adjusts automatically
// Less maintainable: type appears twice
long *value2 = malloc(sizeof(long));
// If you change 'long' to 'double' in declaration,
// you must also change the sizeof - easy to forget!
if (value && value2) {
*value = 12345L;
*value2 = 67890L;
printf("Using sizeof(*ptr): %ld\n", *value);
printf("Using sizeof(type): %ld\n", *value2);
}
free(value);
free(value2);
printf("\n");
}
/**
* =============================================================================
* EXAMPLE 3: Allocating Arrays
* =============================================================================
* Shows how to dynamically allocate arrays.
*/
void example3_array_allocation(void) {
printf("=== EXAMPLE 3: Allocating Arrays ===\n\n");
// -------------------------------------------------------------------------
// Integer array
// -------------------------------------------------------------------------
printf("--- Integer Array ---\n");
int n = 10;
int *intArray = malloc(n * sizeof(*intArray));
if (intArray == NULL) {
printf("Allocation failed!\n");
return;
}
// Fill with squares
for (int i = 0; i < n; i++) {
intArray[i] = i * i;
}
printf("Array of %d integers: ", n);
for (int i = 0; i < n; i++) {
printf("%d ", intArray[i]);
}
printf("\n");
printf("Total size: %zu bytes\n\n", n * sizeof(*intArray));
free(intArray);
// -------------------------------------------------------------------------
// Character array (string buffer)
// -------------------------------------------------------------------------
printf("--- Character Array (String Buffer) ---\n");
size_t bufferSize = 100;
char *buffer = malloc(bufferSize * sizeof(*buffer));
if (buffer != NULL) {
strcpy(buffer, "Hello, Dynamic World!");
printf("Buffer: \"%s\"\n", buffer);
printf("Buffer size: %zu bytes\n\n", bufferSize);
free(buffer);
}
// -------------------------------------------------------------------------
// Double array for calculations
// -------------------------------------------------------------------------
printf("--- Double Array ---\n");
int numValues = 5;
double *values = malloc(numValues * sizeof(*values));
if (values != NULL) {
// Initialize with some values
values[0] = 1.1;
values[1] = 2.2;
values[2] = 3.3;
values[3] = 4.4;
values[4] = 5.5;
// Calculate sum
double sum = 0;
for (int i = 0; i < numValues; i++) {
sum += values[i];
}
printf("Values: ");
for (int i = 0; i < numValues; i++) {
printf("%.1f ", values[i]);
}
printf("\n");
printf("Sum: %.1f\n", sum);
printf("Total size: %zu bytes\n\n", numValues * sizeof(*values));
free(values);
}
}
/**
* =============================================================================
* EXAMPLE 4: Allocating Strings
* =============================================================================
* Demonstrates string allocation patterns.
*/
void example4_string_allocation(void) {
printf("=== EXAMPLE 4: Allocating Strings ===\n\n");
// -------------------------------------------------------------------------
// Exact-size string allocation
// -------------------------------------------------------------------------
printf("--- Exact-Size String Allocation ---\n");
const char *original = "Hello, World!";
size_t length = strlen(original) + 1; // +1 for null terminator
char *copy = malloc(length);
if (copy != NULL) {
strcpy(copy, original);
printf("Original: \"%s\" (static)\n", original);
printf("Copy: \"%s\" (dynamic, %zu bytes)\n", copy, length);
free(copy);
}
printf("\n");
// -------------------------------------------------------------------------
// String concatenation
// -------------------------------------------------------------------------
printf("--- String Concatenation ---\n");
const char *first = "Hello, ";
const char *second = "World!";
size_t totalLen = strlen(first) + strlen(second) + 1;
char *combined = malloc(totalLen);
if (combined != NULL) {
strcpy(combined, first);
strcat(combined, second);
printf("First: \"%s\"\n", first);
printf("Second: \"%s\"\n", second);
printf("Combined: \"%s\" (%zu bytes)\n", combined, totalLen);
free(combined);
}
printf("\n");
// -------------------------------------------------------------------------
// Building string dynamically
// -------------------------------------------------------------------------
printf("--- Building String with sprintf ---\n");
int id = 42;
const char *name = "Alice";
float score = 95.5f;
// Calculate needed size
int neededSize = snprintf(NULL, 0, "ID: %d, Name: %s, Score: %.1f",
id, name, score);
char *formatted = malloc(neededSize + 1); // +1 for null terminator
if (formatted != NULL) {
sprintf(formatted, "ID: %d, Name: %s, Score: %.1f", id, name, score);
printf("Formatted: \"%s\"\n", formatted);
printf("Size: %d bytes\n", neededSize + 1);
free(formatted);
}
printf("\n");
}
/**
* =============================================================================
* EXAMPLE 5: Allocating Structures
* =============================================================================
* Shows how to allocate memory for structures.
*/
void example5_structure_allocation(void) {
printf("=== EXAMPLE 5: Allocating Structures ===\n\n");
// -------------------------------------------------------------------------
// Simple structure
// -------------------------------------------------------------------------
printf("--- Simple Structure ---\n");
typedef struct {
int x;
int y;
} Point;
Point *p = malloc(sizeof(*p));
if (p != NULL) {
p->x = 10;
p->y = 20;
printf("Point: (%d, %d)\n", p->x, p->y);
printf("Size: %zu bytes\n\n", sizeof(*p));
free(p);
}
// -------------------------------------------------------------------------
// Structure with array member
// -------------------------------------------------------------------------
printf("--- Structure with Array Member ---\n");
typedef struct {
int id;
char name[50];
double salary;
} Employee;
Employee *emp = malloc(sizeof(*emp));
if (emp != NULL) {
emp->id = 101;
strcpy(emp->name, "John Doe");
emp->salary = 75000.50;
printf("Employee:\n");
printf(" ID: %d\n", emp->id);
printf(" Name: %s\n", emp->name);
printf(" Salary: $%.2f\n", emp->salary);
printf("Size: %zu bytes\n\n", sizeof(*emp));
free(emp);
}
// -------------------------------------------------------------------------
// Structure with pointer members
// -------------------------------------------------------------------------
printf("--- Structure with Pointer Members ---\n");
typedef struct {
char *name; // Pointer - needs separate allocation
int *scores; // Pointer - needs separate allocation
int numScores;
} Student;
Student *s = malloc(sizeof(*s));
if (s != NULL) {
// Allocate name
const char *studentName = "Alice Smith";
s->name = malloc(strlen(studentName) + 1);
if (s->name != NULL) {
strcpy(s->name, studentName);
}
// Allocate scores array
s->numScores = 5;
s->scores = malloc(s->numScores * sizeof(*s->scores));
if (s->scores != NULL) {
int grades[] = {85, 90, 78, 92, 88};
for (int i = 0; i < s->numScores; i++) {
s->scores[i] = grades[i];
}
}
// Print student info
printf("Student:\n");
printf(" Name: %s\n", s->name ? s->name : "(null)");
printf(" Scores: ");
if (s->scores) {
for (int i = 0; i < s->numScores; i++) {
printf("%d ", s->scores[i]);
}
}
printf("\n\n");
// Free in reverse order of allocation
free(s->scores);
free(s->name);
free(s);
printf("Note: Free pointer members BEFORE freeing the structure!\n");
}
printf("\n");
}
/**
* =============================================================================
* EXAMPLE 6: Array of Structures
* =============================================================================
* Demonstrates allocating arrays of structures.
*/
void example6_array_of_structures(void) {
printf("=== EXAMPLE 6: Array of Structures ===\n\n");
typedef struct {
int id;
char name[30];
float price;
} Product;
int numProducts = 3;
// Allocate array of structures
Product *products = malloc(numProducts * sizeof(*products));
if (products == NULL) {
printf("Allocation failed!\n");
return;
}
// Initialize products
products[0] = (Product){101, "Laptop", 999.99f};
products[1] = (Product){102, "Mouse", 29.99f};
products[2] = (Product){103, "Keyboard", 79.99f};
// Print all products
printf("Product List:\n");
printf("%-6s %-20s %10s\n", "ID", "Name", "Price");
printf("------------------------------------------\n");
for (int i = 0; i < numProducts; i++) {
printf("%-6d %-20s $%9.2f\n",
products[i].id,
products[i].name,
products[i].price);
}
printf("\nTotal allocation: %zu bytes\n", numProducts * sizeof(*products));
printf("Per item: %zu bytes\n\n", sizeof(*products));
free(products);
}
/**
* =============================================================================
* EXAMPLE 7: Error Handling Patterns
* =============================================================================
* Shows different approaches to handling allocation failures.
*/
void example7_error_handling(void) {
printf("=== EXAMPLE 7: Error Handling Patterns ===\n\n");
// -------------------------------------------------------------------------
// Pattern 1: Simple NULL check
// -------------------------------------------------------------------------
printf("--- Pattern 1: Simple NULL Check ---\n");
int *p1 = malloc(sizeof(int));
if (p1 == NULL) {
fprintf(stderr, "Error: Memory allocation failed\n");
// Handle error appropriately
} else {
*p1 = 42;
printf("Allocated: %d\n", *p1);
free(p1);
}
printf("\n");
// -------------------------------------------------------------------------
// Pattern 2: Multiple allocations with cleanup
// -------------------------------------------------------------------------
printf("--- Pattern 2: Multiple Allocations with Cleanup ---\n");
int *a = NULL, *b = NULL, *c = NULL;
a = malloc(sizeof(*a));
if (a == NULL) goto cleanup;
b = malloc(sizeof(*b));
if (b == NULL) goto cleanup;
c = malloc(sizeof(*c));
if (c == NULL) goto cleanup;
// All allocations succeeded
*a = 1; *b = 2; *c = 3;
printf("All allocations succeeded: %d, %d, %d\n", *a, *b, *c);
cleanup:
// free(NULL) is safe, so this always works
free(c);
free(b);
free(a);
printf("Cleanup complete\n\n");
// -------------------------------------------------------------------------
// Pattern 3: Safe wrapper function
// -------------------------------------------------------------------------
printf("--- Pattern 3: Safe Wrapper Function ---\n");
printf("Implementation:\n");
printf(" void *safe_malloc(size_t size) {\n");
printf(" void *ptr = malloc(size);\n");
printf(" if (ptr == NULL && size != 0) {\n");
printf(" fprintf(stderr, \"Fatal: Out of memory\\n\");\n");
printf(" exit(EXIT_FAILURE);\n");
printf(" }\n");
printf(" return ptr;\n");
printf(" }\n\n");
}
/**
* =============================================================================
* EXAMPLE 8: Memory Lifecycle
* =============================================================================
* Traces memory through allocation, use, and deallocation.
*/
void example8_memory_lifecycle(void) {
printf("=== EXAMPLE 8: Memory Lifecycle ===\n\n");
printf("--- Step 1: Allocation ---\n");
int *data = malloc(5 * sizeof(*data));
printf("malloc() called: allocated %zu bytes at %p\n",
5 * sizeof(*data), (void*)data);
if (data == NULL) {
printf("Allocation failed!\n");
return;
}
printf("\n--- Step 2: Initialization ---\n");
printf("Before init (garbage values may crash if accessed):\n");
// Note: Reading uninitialized memory is undefined behavior
// We'll initialize immediately for safety
for (int i = 0; i < 5; i++) {
data[i] = (i + 1) * 10;
}
printf("After init: ");
for (int i = 0; i < 5; i++) {
printf("%d ", data[i]);
}
printf("\n");
printf("\n--- Step 3: Usage ---\n");
int sum = 0;
for (int i = 0; i < 5; i++) {
sum += data[i];
}
printf("Sum of elements: %d\n", sum);
// Modify some values
data[2] = 999;
printf("After modification: ");
for (int i = 0; i < 5; i++) {
printf("%d ", data[i]);
}
printf("\n");
printf("\n--- Step 4: Deallocation ---\n");
printf("Calling free() on %p\n", (void*)data);
free(data);
printf("Memory freed\n");
printf("\n--- Step 5: Pointer Cleanup ---\n");
printf("Before: data = %p (dangling!)\n", (void*)data);
data = NULL;
printf("After: data = %p (safe)\n", (void*)data);
printf("\nFull lifecycle complete.\n\n");
}
/**
* =============================================================================
* EXAMPLE 9: What Happens After free()
* =============================================================================
* Demonstrates why using freed memory is dangerous.
*/
void example9_after_free(void) {
printf("=== EXAMPLE 9: What Happens After free() ===\n\n");
printf("After free():\n");
printf("1. Memory is returned to the heap manager\n");
printf("2. Memory may be reused for other allocations\n");
printf("3. The pointer still holds the old address (dangling)\n");
printf("4. The memory contents may or may not change\n");
printf("5. Accessing freed memory is UNDEFINED BEHAVIOR\n\n");
// Demonstrate with safe observation
int *p = malloc(sizeof(int));
if (p != NULL) {
*p = 12345;
printf("Before free: *p = %d, p = %p\n", *p, (void*)p);
free(p);
printf("After free: p = %p (same address, but invalid!)\n", (void*)p);
printf(" Accessing *p would be undefined behavior\n\n");
// Set to NULL for safety
p = NULL;
printf("After NULL: p = %p (now safe)\n", (void*)p);
}
printf("\n--- Why NULL Helps ---\n");
printf("if (p != NULL) {\n");
printf(" *p = 10; // Safe: we know p is valid\n");
printf("}\n");
printf("If p was NULL, the check prevents the crash.\n");
printf("If p was dangling, no check can help - bug in code!\n\n");
}
/**
* =============================================================================
* EXAMPLE 10: Factory Pattern
* =============================================================================
* Functions that allocate and return memory.
*/
// Factory function to create a string copy
char *createStringCopy(const char *source) {
if (source == NULL) return NULL;
size_t length = strlen(source) + 1;
char *copy = malloc(length);
if (copy != NULL) {
strcpy(copy, source);
}
return copy; // Caller must free
}
// Factory function to create an integer array
int *createIntArray(int size, int initialValue) {
if (size <= 0) return NULL;
int *array = malloc(size * sizeof(*array));
if (array != NULL) {
for (int i = 0; i < size; i++) {
array[i] = initialValue;
}
}
return array; // Caller must free
}
// Factory function for a structure
typedef struct {
int id;
char *name;
int age;
} PersonEx;
PersonEx *createPersonEx(int id, const char *name, int age) {
PersonEx *p = malloc(sizeof(*p));
if (p == NULL) return NULL;
p->id = id;
p->age = age;
p->name = createStringCopy(name);
if (p->name == NULL) {
free(p);
return NULL;
}
return p; // Caller must call destroyPerson
}
void destroyPersonEx(PersonEx *p) {
if (p != NULL) {
free(p->name);
free(p);
}
}
void example10_factory_pattern(void) {
printf("=== EXAMPLE 10: Factory Pattern ===\n\n");
// -------------------------------------------------------------------------
// Using string factory
// -------------------------------------------------------------------------
printf("--- String Factory ---\n");
char *str = createStringCopy("Hello from factory!");
if (str != NULL) {
printf("Created: \"%s\"\n", str);
free(str);
}
printf("\n");
// -------------------------------------------------------------------------
// Using array factory
// -------------------------------------------------------------------------
printf("--- Array Factory ---\n");
int *arr = createIntArray(5, 42);
if (arr != NULL) {
printf("Created array: ");
for (int i = 0; i < 5; i++) {
printf("%d ", arr[i]);
}
printf("\n");
free(arr);
}
printf("\n");
// -------------------------------------------------------------------------
// Using structure factory
// -------------------------------------------------------------------------
printf("--- Structure Factory ---\n");
PersonEx *person = createPersonEx(1, "Alice", 25);
if (person != NULL) {
printf("Created Person:\n");
printf(" ID: %d\n", person->id);
printf(" Name: %s\n", person->name);
printf(" Age: %d\n", person->age);
destroyPersonEx(person);
}
printf("\n");
// -------------------------------------------------------------------------
// Key points
// -------------------------------------------------------------------------
printf("--- Factory Pattern Rules ---\n");
printf("1. Document that caller must free/destroy\n");
printf("2. Provide matching destroy function for complex types\n");
printf("3. Check for NULL return to detect failures\n");
printf("4. Handle nested allocation failures properly\n");
printf("\n");
}
/**
* =============================================================================
* EXAMPLE 11: 2D Array Allocation
* =============================================================================
* Different approaches to allocating 2D arrays.
*/
void example11_2d_arrays(void) {
printf("=== EXAMPLE 11: 2D Array Allocation ===\n\n");
int rows = 3, cols = 4;
// -------------------------------------------------------------------------
// Method 1: Array of pointers (flexible rows)
// -------------------------------------------------------------------------
printf("--- Method 1: Array of Pointers ---\n");
int **matrix1 = malloc(rows * sizeof(*matrix1));
if (matrix1 != NULL) {
// Allocate each row
int allocFailed = 0;
for (int i = 0; i < rows && !allocFailed; i++) {
matrix1[i] = malloc(cols * sizeof(**matrix1));
if (matrix1[i] == NULL) {
allocFailed = 1;
// Free previously allocated rows
while (--i >= 0) free(matrix1[i]);
}
}
if (!allocFailed) {
// Fill with values
int val = 1;
for (int i = 0; i < rows; i++) {
for (int j = 0; j < cols; j++) {
matrix1[i][j] = val++;
}
}
printf("Matrix (%d x %d):\n", rows, cols);
for (int i = 0; i < rows; i++) {
printf(" ");
for (int j = 0; j < cols; j++) {
printf("%3d ", matrix1[i][j]);
}
printf("\n");
}
// Free all rows, then the row pointer array
for (int i = 0; i < rows; i++) {
free(matrix1[i]);
}
}
free(matrix1);
}
printf("\n");
// -------------------------------------------------------------------------
// Method 2: Contiguous block (better cache performance)
// -------------------------------------------------------------------------
printf("--- Method 2: Contiguous Block ---\n");
int *matrix2 = malloc(rows * cols * sizeof(*matrix2));
if (matrix2 != NULL) {
// Fill with values (access as matrix2[i * cols + j])
int val = 1;
for (int i = 0; i < rows; i++) {
for (int j = 0; j < cols; j++) {
matrix2[i * cols + j] = val++;
}
}
printf("Matrix (%d x %d):\n", rows, cols);
for (int i = 0; i < rows; i++) {
printf(" ");
for (int j = 0; j < cols; j++) {
printf("%3d ", matrix2[i * cols + j]);
}
printf("\n");
}
// Single free for entire matrix
free(matrix2);
}
printf("\nComparison:\n");
printf(" Array of pointers: Flexible row sizes, more memory overhead\n");
printf(" Contiguous block: Fixed row size, better cache performance\n");
printf("\n");
}
/**
* =============================================================================
* EXAMPLE 12: Common Mistakes (Educational)
* =============================================================================
* Shows common mistakes and their corrections.
*/
void example12_common_mistakes(void) {
printf("=== EXAMPLE 12: Common Mistakes (Educational) ===\n\n");
// -------------------------------------------------------------------------
// Mistake 1: Wrong sizeof
// -------------------------------------------------------------------------
printf("--- Mistake 1: Wrong sizeof ---\n");
printf("WRONG: int *arr = malloc(10 * sizeof(int*)); // Pointer size!\n");
printf("RIGHT: int *arr = malloc(10 * sizeof(int)); // Element size\n");
printf("BEST: int *arr = malloc(10 * sizeof(*arr)); // Automatic\n\n");
// -------------------------------------------------------------------------
// Mistake 2: Forgetting +1 for strings
// -------------------------------------------------------------------------
printf("--- Mistake 2: Forgetting String Terminator ---\n");
printf("WRONG: char *s = malloc(strlen(str)); // No room for '\\0'!\n");
printf("RIGHT: char *s = malloc(strlen(str) + 1); // Includes '\\0'\n\n");
// -------------------------------------------------------------------------
// Mistake 3: Not checking NULL
// -------------------------------------------------------------------------
printf("--- Mistake 3: Not Checking NULL ---\n");
printf("WRONG:\n");
printf(" int *p = malloc(SIZE);\n");
printf(" *p = 10; // CRASH if malloc failed!\n");
printf("RIGHT:\n");
printf(" int *p = malloc(SIZE);\n");
printf(" if (p == NULL) { /* handle error */ }\n");
printf(" *p = 10;\n\n");
// -------------------------------------------------------------------------
// Mistake 4: Memory leak on reallocation
// -------------------------------------------------------------------------
printf("--- Mistake 4: Memory Leak with Pointer Reassignment ---\n");
printf("WRONG:\n");
printf(" int *p = malloc(100);\n");
printf(" p = malloc(200); // First allocation leaked!\n");
printf("RIGHT:\n");
printf(" int *p = malloc(100);\n");
printf(" free(p);\n");
printf(" p = malloc(200); // Or use realloc\n\n");
// -------------------------------------------------------------------------
// Mistake 5: Using freed memory
// -------------------------------------------------------------------------
printf("--- Mistake 5: Using Freed Memory ---\n");
printf("WRONG:\n");
printf(" free(p);\n");
printf(" printf(\"%%d\", *p); // Undefined behavior!\n");
printf("RIGHT:\n");
printf(" printf(\"%%d\", *p); // Use before free\n");
printf(" free(p);\n");
printf(" p = NULL; // Prevent accidental use\n\n");
// -------------------------------------------------------------------------
// Mistake 6: Double free
// -------------------------------------------------------------------------
printf("--- Mistake 6: Double Free ---\n");
printf("WRONG:\n");
printf(" free(p);\n");
printf(" free(p); // Undefined behavior!\n");
printf("RIGHT:\n");
printf(" free(p);\n");
printf(" p = NULL;\n");
printf(" free(p); // Safe: free(NULL) is no-op\n\n");
}
/**
* =============================================================================
* MAIN FUNCTION
* =============================================================================
*/
int main(void) {
printf("****************************************************************\n");
printf("* MALLOC() AND FREE() - EXAMPLES *\n");
printf("****************************************************************\n\n");
example1_basic_malloc_free();
example2_sizeof_usage();
example3_array_allocation();
example4_string_allocation();
example5_structure_allocation();
example6_array_of_structures();
example7_error_handling();
example8_memory_lifecycle();
example9_after_free();
example10_factory_pattern();
example11_2d_arrays();
example12_common_mistakes();
printf("****************************************************************\n");
printf("* EXAMPLES COMPLETE *\n");
printf("****************************************************************\n");
return 0;
}