Dynamic Memory Allocation in C: Complete Guide

Introduction to Dynamic Memory Allocation

Dynamic memory allocation allows programs to request memory at runtime, providing flexibility to handle data structures whose size isn't known at compile time. Unlike static memory allocation (stack), dynamic memory is allocated on the heap and must be manually managed.


System Architecture Overview

Dynamic Memory Architecture ├── Memory Segments │ ├── Stack (Local variables, function calls) │ ├── Heap (Dynamic allocations) │ ├── Data (Global/Static variables) │ └── Text (Program code) ├── Allocation Functions │ ├── malloc() - Allocate uninitialized memory │ ├── calloc() - Allocate and initialize to zero │ ├── realloc() - Resize existing allocation │ └── free() - Release allocated memory └── Memory Management ├── Allocation Tracking ├── Memory Leak Prevention ├── Dangling Pointer Prevention └── Fragmentation Handling

Core Memory Allocation Functions

1. malloc() - Memory Allocation

#include <stdio.h> #include <stdlib.h> int main() { int *ptr; int n = 5; // Allocate memory for 5 integers ptr = (int*)malloc(n * sizeof(int)); // Check if allocation succeeded if (ptr == NULL) { printf("Memory allocation failed!\n"); return 1; } printf("Memory allocated successfully\n"); // Use the allocated memory for (int i = 0; i < n; i++) { ptr[i] = i * 10; printf("ptr[%d] = %d\n", i, ptr[i]); } // Free the allocated memory free(ptr); return 0; }

Output:

Memory allocated successfully ptr[0] = 0 ptr[1] = 10 ptr[2] = 20 ptr[3] = 30 ptr[4] = 40

2. calloc() - Contiguous Allocation

#include <stdio.h> #include <stdlib.h> int main() { int *ptr; int n = 5; // Allocate and zero-initialize memory for 5 integers ptr = (int*)calloc(n, sizeof(int)); if (ptr == NULL) { printf("Memory allocation failed!\n"); return 1; } // calloc initializes all bytes to zero printf("calloc initializes to zero:\n"); for (int i = 0; i < n; i++) { printf("ptr[%d] = %d\n", i, ptr[i]); } free(ptr); return 0; }

Output:

calloc initializes to zero: ptr[0] = 0 ptr[1] = 0 ptr[2] = 0 ptr[3] = 0 ptr[4] = 0

3. realloc() - Reallocation

#include <stdio.h> #include <stdlib.h> int main() { int *ptr; int n = 5; // Initial allocation ptr = (int*)malloc(n * sizeof(int)); if (ptr == NULL) { printf("Initial allocation failed!\n"); return 1; } // Initialize data for (int i = 0; i < n; i++) { ptr[i] = i + 1; } printf("Original array (%d elements):\n", n); for (int i = 0; i < n; i++) { printf("%d ", ptr[i]); } printf("\n"); // Resize to 10 elements int new_size = 10; int *new_ptr = (int*)realloc(ptr, new_size * sizeof(int)); if (new_ptr == NULL) { printf("Reallocation failed!\n"); free(ptr); return 1; } ptr = new_ptr; // Initialize new elements for (int i = n; i < new_size; i++) { ptr[i] = i + 1; } printf("Resized array (%d elements):\n", new_size); for (int i = 0; i < new_size; i++) { printf("%d ", ptr[i]); } printf("\n"); free(ptr); return 0; }

Output:

Original array (5 elements): 1 2 3 4 5 Resized array (10 elements): 1 2 3 4 5 6 7 8 9 10

4. free() - Memory Deallocation

#include <stdio.h> #include <stdlib.h> int main() { int *ptr1, *ptr2, *ptr3; // Multiple allocations ptr1 = (int*)malloc(100 * sizeof(int)); ptr2 = (int*)calloc(50, sizeof(int)); ptr3 = (int*)malloc(200 * sizeof(int)); if (ptr1 && ptr2 && ptr3) { printf("All allocations successful\n"); // Use memory... // Free in reverse order (good practice) free(ptr3); free(ptr2); free(ptr1); printf("All memory freed\n"); } else { // Clean up any successful allocations if some failed if (ptr1) free(ptr1); if (ptr2) free(ptr2); if (ptr3) free(ptr3); printf("Some allocations failed\n"); } return 0; }

Common Dynamic Data Structures

1. Dynamic Array

#include <stdio.h> #include <stdlib.h> typedef struct { int *data; int size; int capacity; } DynamicArray; // Initialize dynamic array DynamicArray* createArray(int initialCapacity) { DynamicArray *arr = (DynamicArray*)malloc(sizeof(DynamicArray)); if (arr == NULL) return NULL; arr->data = (int*)malloc(initialCapacity * sizeof(int)); if (arr->data == NULL) { free(arr); return NULL; } arr->size = 0; arr->capacity = initialCapacity; return arr; } // Add element to array void append(DynamicArray *arr, int value) { if (arr->size >= arr->capacity) { // Double the capacity arr->capacity *= 2; arr->data = (int*)realloc(arr->data, arr->capacity * sizeof(int)); if (arr->data == NULL) { printf("Failed to resize array!\n"); return; } printf("Array resized to capacity: %d\n", arr->capacity); } arr->data[arr->size++] = value; } // Get element at index int get(DynamicArray *arr, int index) { if (index < 0 || index >= arr->size) { printf("Index out of bounds!\n"); return -1; } return arr->data[index]; } // Free array void freeArray(DynamicArray *arr) { if (arr) { free(arr->data); free(arr); } } // Print array void printArray(DynamicArray *arr) { printf("Array (size=%d, capacity=%d): ", arr->size, arr->capacity); for (int i = 0; i < arr->size; i++) { printf("%d ", arr->data[i]); } printf("\n"); } int main() { DynamicArray *arr = createArray(3); printf("Appending elements:\n"); for (int i = 1; i <= 10; i++) { append(arr, i * 10); printf("Appended %d\n", i * 10); } printArray(arr); printf("\nElement at index 5: %d\n", get(arr, 5)); freeArray(arr); return 0; }

2. Singly Linked List

#include <stdio.h> #include <stdlib.h> // Node structure typedef struct Node { int data; struct Node *next; } Node; // List structure typedef struct { Node *head; Node *tail; int size; } LinkedList; // Create new node Node* createNode(int data) { Node *newNode = (Node*)malloc(sizeof(Node)); if (newNode == NULL) return NULL; newNode->data = data; newNode->next = NULL; return newNode; } // Initialize list LinkedList* createList() { LinkedList *list = (LinkedList*)malloc(sizeof(LinkedList)); if (list == NULL) return NULL; list->head = NULL; list->tail = NULL; list->size = 0; return list; } // Insert at beginning void insertAtBeginning(LinkedList *list, int data) { Node *newNode = createNode(data); if (newNode == NULL) return; newNode->next = list->head; list->head = newNode; if (list->tail == NULL) { list->tail = newNode; } list->size++; } // Insert at end void insertAtEnd(LinkedList *list, int data) { Node *newNode = createNode(data); if (newNode == NULL) return; if (list->tail == NULL) { list->head = list->tail = newNode; } else { list->tail->next = newNode; list->tail = newNode; } list->size++; } // Delete first node int deleteFromBeginning(LinkedList *list) { if (list->head == NULL) return -1; Node *temp = list->head; int data = temp->data; list->head = list->head->next; if (list->head == NULL) { list->tail = NULL; } free(temp); list->size--; return data; } // Search for element Node* search(LinkedList *list, int key) { Node *current = list->head; while (current != NULL) { if (current->data == key) { return current; } current = current->next; } return NULL; } // Print list void printList(LinkedList *list) { Node *current = list->head; printf("List (size=%d): ", list->size); while (current != NULL) { printf("%d -> ", current->data); current = current->next; } printf("NULL\n"); } // Free entire list void freeList(LinkedList *list) { Node *current = list->head; Node *next; while (current != NULL) { next = current->next; free(current); current = next; } free(list); } int main() { LinkedList *list = createList(); printf("Inserting at beginning:\n"); insertAtBeginning(list, 30); insertAtBeginning(list, 20); insertAtBeginning(list, 10); printList(list); printf("\nInserting at end:\n"); insertAtEnd(list, 40); insertAtEnd(list, 50); printList(list); printf("\nSearching for 30:\n"); Node *found = search(list, 30); if (found) { printf("Found: %d\n", found->data); } else { printf("Not found\n"); } printf("\nDeleting from beginning:\n"); int deleted = deleteFromBeginning(list); printf("Deleted: %d\n", deleted); printList(list); freeList(list); return 0; }

3. Dynamic 2D Array

#include <stdio.h> #include <stdlib.h> // Function to create a 2D array int** create2DArray(int rows, int cols) { // Allocate memory for row pointers int **arr = (int**)malloc(rows * sizeof(int*)); if (arr == NULL) return NULL; // Allocate memory for each row for (int i = 0; i < rows; i++) { arr[i] = (int*)malloc(cols * sizeof(int)); if (arr[i] == NULL) { // Clean up previously allocated rows for (int j = 0; j < i; j++) { free(arr[j]); } free(arr); return NULL; } } return arr; } // Function to initialize 2D array void initialize2DArray(int **arr, int rows, int cols) { for (int i = 0; i < rows; i++) { for (int j = 0; j < cols; j++) { arr[i][j] = i * cols + j + 1; } } } // Function to print 2D array void print2DArray(int **arr, int rows, int cols) { printf("2D Array (%d x %d):\n", rows, cols); for (int i = 0; i < rows; i++) { for (int j = 0; j < cols; j++) { printf("%4d ", arr[i][j]); } printf("\n"); } } // Function to free 2D array void free2DArray(int **arr, int rows) { for (int i = 0; i < rows; i++) { free(arr[i]); } free(arr); } int main() { int rows = 3, cols = 4; int **matrix = create2DArray(rows, cols); if (matrix == NULL) { printf("Failed to allocate 2D array!\n"); return 1; } initialize2DArray(matrix, rows, cols); print2DArray(matrix, rows, cols); free2DArray(matrix, rows); return 0; }

4. Dynamic String Array

#include <stdio.h> #include <stdlib.h> #include <string.h> typedef struct { char **strings; int size; int capacity; } StringArray; // Initialize string array StringArray* createStringArray(int initialCapacity) { StringArray *arr = (StringArray*)malloc(sizeof(StringArray)); if (arr == NULL) return NULL; arr->strings = (char**)malloc(initialCapacity * sizeof(char*)); if (arr->strings == NULL) { free(arr); return NULL; } arr->size = 0; arr->capacity = initialCapacity; return arr; } // Add string to array void addString(StringArray *arr, const char *str) { if (arr->size >= arr->capacity) { arr->capacity *= 2; arr->strings = (char**)realloc(arr->strings, arr->capacity * sizeof(char*)); if (arr->strings == NULL) { printf("Failed to resize string array!\n"); return; } } arr->strings[arr->size] = (char*)malloc(strlen(str) + 1); if (arr->strings[arr->size] != NULL) { strcpy(arr->strings[arr->size], str); arr->size++; } } // Remove string at index void removeString(StringArray *arr, int index) { if (index < 0 || index >= arr->size) return; free(arr->strings[index]); // Shift remaining strings for (int i = index; i < arr->size - 1; i++) { arr->strings[i] = arr->strings[i + 1]; } arr->size--; } // Print all strings void printStringArray(StringArray *arr) { printf("String Array (size=%d):\n", arr->size); for (int i = 0; i < arr->size; i++) { printf(" [%d]: %s\n", i, arr->strings[i]); } } // Free string array void freeStringArray(StringArray *arr) { for (int i = 0; i < arr->size; i++) { free(arr->strings[i]); } free(arr->strings); free(arr); } int main() { StringArray *arr = createStringArray(3); addString(arr, "Hello"); addString(arr, "World"); addString(arr, "Dynamic"); addString(arr, "Memory"); addString(arr, "Allocation"); printStringArray(arr); printf("\nRemoving index 2...\n"); removeString(arr, 2); printStringArray(arr); freeStringArray(arr); return 0; }

Advanced Memory Management Techniques

1. Memory Pool

#include <stdio.h> #include <stdlib.h> #include <stdint.h> typedef struct { void *pool; size_t blockSize; size_t totalBlocks; size_t usedBlocks; uint8_t *bitmap; } MemoryPool; // Create memory pool MemoryPool* createPool(size_t blockSize, size_t totalBlocks) { MemoryPool *pool = (MemoryPool*)malloc(sizeof(MemoryPool)); if (pool == NULL) return NULL; pool->pool = malloc(blockSize * totalBlocks); if (pool->pool == NULL) { free(pool); return NULL; } pool->bitmap = (uint8_t*)calloc((totalBlocks + 7) / 8, sizeof(uint8_t)); if (pool->bitmap == NULL) { free(pool->pool); free(pool); return NULL; } pool->blockSize = blockSize; pool->totalBlocks = totalBlocks; pool->usedBlocks = 0; return pool; } // Allocate from pool void* poolAlloc(MemoryPool *pool) { if (pool->usedBlocks >= pool->totalBlocks) { return NULL; // Pool full } // Find free block for (size_t i = 0; i < pool->totalBlocks; i++) { size_t byteIndex = i / 8; size_t bitIndex = i % 8; if (!(pool->bitmap[byteIndex] & (1 << bitIndex))) { // Mark as used pool->bitmap[byteIndex] |= (1 << bitIndex); pool->usedBlocks++; return (char*)pool->pool + (i * pool->blockSize); } } return NULL; } // Free from pool void poolFree(MemoryPool *pool, void *ptr) { if (ptr == NULL) return; // Calculate block index ptrdiff_t offset = (char*)ptr - (char*)pool->pool; if (offset < 0 || offset >= (ptrdiff_t)(pool->blockSize * pool->totalBlocks)) { return; // Pointer not from this pool } size_t index = offset / pool->blockSize; // Mark as free size_t byteIndex = index / 8; size_t bitIndex = index % 8; pool->bitmap[byteIndex] &= ~(1 << bitIndex); pool->usedBlocks--; } // Destroy pool void destroyPool(MemoryPool *pool) { if (pool) { free(pool->bitmap); free(pool->pool); free(pool); } } int main() { // Create pool of 10 blocks, each 32 bytes MemoryPool *pool = createPool(32, 10); if (pool == NULL) { printf("Failed to create pool\n"); return 1; } printf("Pool created: %zu blocks of %zu bytes\n", pool->totalBlocks, pool->blockSize); // Allocate some blocks void *ptr1 = poolAlloc(pool); void *ptr2 = poolAlloc(pool); void *ptr3 = poolAlloc(pool); printf("Allocated 3 blocks\n"); printf("Used blocks: %zu\n", pool->usedBlocks); // Free a block poolFree(pool, ptr2); printf("Freed 1 block\n"); printf("Used blocks: %zu\n", pool->usedBlocks); // Allocate another void *ptr4 = poolAlloc(pool); printf("Allocated another block\n"); printf("Used blocks: %zu\n", pool->usedBlocks); destroyPool(pool); return 0; }

2. Reference Counting

#include <stdio.h> #include <stdlib.h> #include <string.h> typedef struct { int refCount; char data[]; } RefCounted; // Create reference counted object RefCounted* createRefCounted(const char *str) { size_t len = strlen(str) + 1; RefCounted *obj = (RefCounted*)malloc(sizeof(RefCounted) + len); if (obj) { obj->refCount = 1; strcpy(obj->data, str); } return obj; } // Increment reference count void retain(RefCounted *obj) { if (obj) { obj->refCount++; } } // Decrement reference count and free if zero void release(RefCounted *obj) { if (obj && --obj->refCount == 0) { printf("Freeing object: %s\n", obj->data); free(obj); } } int main() { RefCounted *obj1 = createRefCounted("Hello, World!"); printf("obj1 refCount: %d\n", obj1->refCount); printf("obj1 data: %s\n", obj1->data); // Create another reference RefCounted *obj2 = obj1; retain(obj2); printf("After retain, obj1 refCount: %d\n", obj1->refCount); // Release one reference release(obj1); printf("After first release, obj2 refCount: %d\n", obj2->refCount); // Release final reference release(obj2); return 0; }

3. Memory Leak Detection

#include <stdio.h> #include <stdlib.h> #include <string.h> #ifdef DEBUG_MEMORY typedef struct Allocation { void *ptr; size_t size; const char *file; int line; struct Allocation *next; } Allocation; static Allocation *allocations = NULL; static size_t totalAllocated = 0; static size_t totalFreed = 0; // Track allocation void* trackMalloc(size_t size, const char *file, int line) { void *ptr = malloc(size); if (ptr) { Allocation *alloc = malloc(sizeof(Allocation)); if (alloc) { alloc->ptr = ptr; alloc->size = size; alloc->file = file; alloc->line = line; alloc->next = allocations; allocations = alloc; totalAllocated += size; } } return ptr; } // Track free void trackFree(void *ptr) { if (ptr) { Allocation **prev = &allocations; Allocation *curr = allocations; while (curr) { if (curr->ptr == ptr) { *prev = curr->next; totalFreed += curr->size; free(curr); break; } prev = &curr->next; curr = curr->next; } free(ptr); } } // Report memory leaks void reportMemoryLeaks() { if (allocations == NULL) { printf("No memory leaks detected!\n"); printf("Total allocated: %zu bytes\n", totalAllocated); printf("Total freed: %zu bytes\n", totalFreed); return; } printf("\n=== MEMORY LEAK REPORT ===\n"); printf("Leaked allocations:\n"); Allocation *curr = allocations; while (curr) { printf(" %zu bytes at %p (allocated at %s:%d)\n", curr->size, curr->ptr, curr->file, curr->line); curr = curr->next; } printf("Total leaked: %zu bytes\n", totalAllocated - totalFreed); printf("========================\n\n"); } #define malloc(size) trackMalloc(size, __FILE__, __LINE__) #define free(ptr) trackFree(ptr) #endif int main() { // Test with leak detection int *arr1 = (int*)malloc(10 * sizeof(int)); int *arr2 = (int*)malloc(20 * sizeof(int)); char *str = (char*)malloc(100 * sizeof(char)); // Use memory for (int i = 0; i < 10; i++) { arr1[i] = i; } // Free only some allocations free(arr1); free(arr2); // Comment this out to create a leak // str is intentionally leaked for demonstration #ifdef DEBUG_MEMORY reportMemoryLeaks(); #endif return 0; }

Common Pitfalls and Best Practices

1. Memory Leaks

#include <stdio.h> #include <stdlib.h> // BAD - Memory leak void badFunction() { int *ptr = (int*)malloc(100 * sizeof(int)); // Use ptr but forget to free // Memory leak! } // GOOD - Proper cleanup void goodFunction() { int *ptr = (int*)malloc(100 * sizeof(int)); if (ptr) { // Use ptr // ... free(ptr); // Don't forget! } } // BAD - Losing pointer void losingPointer() { int *ptr = (int*)malloc(100 * sizeof(int)); ptr = NULL; // Lost reference - can't free now! } int main() { goodFunction(); return 0; }

2. Dangling Pointers

#include <stdio.h> #include <stdlib.h> int main() { int *ptr = (int*)malloc(sizeof(int)); *ptr = 42; printf("Before free: %d\n", *ptr); free(ptr); // BAD - Using after free (dangling pointer) // printf("After free: %d\n", *ptr); // Undefined behavior! // GOOD - Set to NULL after freeing ptr = NULL; // This will crash cleanly instead of undefined behavior // if (ptr) printf("%d\n", *ptr); return 0; }

3. Double Free

#include <stdio.h> #include <stdlib.h> int main() { int *ptr = (int*)malloc(sizeof(int)); free(ptr); // BAD - Double free // free(ptr); // Undefined behavior, may crash // GOOD - Set to NULL after free ptr = NULL; free(ptr); // free(NULL) is safe return 0; }

4. Buffer Overflow

#include <stdio.h> #include <stdlib.h> #include <string.h> int main() { int *arr = (int*)malloc(5 * sizeof(int)); if (arr == NULL) return 1; // BAD - Writing beyond allocated memory for (int i = 0; i <= 5; i++) { // Should be i < 5 arr[i] = i; // Buffer overflow when i = 5 } // GOOD - Proper bounds checking for (int i = 0; i < 5; i++) { arr[i] = i; } free(arr); // String buffer overflow example char *str = (char*)malloc(10 * sizeof(char)); if (str) { // BAD - Copying too much data // strcpy(str, "This string is way too long!"); // GOOD - Using strncpy strncpy(str, "Short", 9); str[9] = '\0'; printf("String: %s\n", str); free(str); } return 0; }

5. Allocation Failure Handling

#include <stdio.h> #include <stdlib.h> void safeMemoryOperations() { int *ptr = (int*)malloc(100 * sizeof(int)); // ALWAYS check allocation success if (ptr == NULL) { printf("Memory allocation failed!\n"); return; } // Use memory... free(ptr); } // Function that allocates multiple blocks int* allocateMultiple() { int *ptr1 = (int*)malloc(100 * sizeof(int)); if (ptr1 == NULL) return NULL; int *ptr2 = (int*)malloc(200 * sizeof(int)); if (ptr2 == NULL) { free(ptr1); // Clean up first allocation return NULL; } int *ptr3 = (int*)malloc(300 * sizeof(int)); if (ptr3 == NULL) { free(ptr1); free(ptr2); return NULL; } // Success - use all three // In real code, you'd return something meaningful free(ptr1); free(ptr2); free(ptr3); return ptr1; // Simplified } int main() { safeMemoryOperations(); allocateMultiple(); return 0; }

Memory Alignment Considerations

#include <stdio.h> #include <stdlib.h> #include <stdalign.h> typedef struct { char c; int i; double d; } Misaligned; typedef struct { double d; int i; char c; } Aligned; int main() { printf("=== Alignment Examples ===\n\n"); printf("Misaligned structure:\n"); printf(" Size: %zu bytes\n", sizeof(Misaligned)); printf(" Offset of c: %zu\n", offsetof(Misaligned, c)); printf(" Offset of i: %zu\n", offsetof(Misaligned, i)); printf(" Offset of d: %zu\n\n", offsetof(Misaligned, d)); printf("Aligned structure:\n"); printf(" Size: %zu bytes\n", sizeof(Aligned)); printf(" Offset of d: %zu\n", offsetof(Aligned, d)); printf(" Offset of i: %zu\n", offsetof(Aligned, i)); printf(" Offset of c: %zu\n\n", offsetof(Aligned, c)); // Allocate aligned memory void *ptr; int err = posix_memalign(&ptr, 64, 1024); // 64-byte alignment if (err == 0) { printf("Allocated 64-byte aligned memory at %p\n", ptr); free(ptr); } return 0; }

Performance Considerations

1. Allocation Overhead

#include <stdio.h> #include <stdlib.h> #include <time.h> #define ITERATIONS 100000 int main() { clock_t start, end; double cpu_time_used; // Measure single large allocation start = clock(); int *large = (int*)malloc(ITERATIONS * sizeof(int)); if (large) { for (int i = 0; i < ITERATIONS; i++) { large[i] = i; } free(large); } end = clock(); cpu_time_used = ((double)(end - start)) / CLOCKS_PER_SEC; printf("Single large allocation: %.4f seconds\n", cpu_time_used); // Measure many small allocations start = clock(); for (int i = 0; i < ITERATIONS; i++) { int *small = (int*)malloc(sizeof(int)); if (small) { *small = i; free(small); } } end = clock(); cpu_time_used = ((double)(end - start)) / CLOCKS_PER_SEC; printf("Many small allocations: %.4f seconds\n", cpu_time_used); return 0; }

2. Realloc Efficiency

#include <stdio.h> #include <stdlib.h> // Inefficient - reallocating one element at a time void inefficientGrowth() { int *arr = NULL; int size = 0; for (int i = 0; i < 10000; i++) { arr = (int*)realloc(arr, (size + 1) * sizeof(int)); if (arr) { arr[size++] = i; } } free(arr); } // Efficient - doubling capacity void efficientGrowth() { int *arr = NULL; int size = 0; int capacity = 0; for (int i = 0; i < 10000; i++) { if (size >= capacity) { capacity = capacity == 0 ? 1 : capacity * 2; arr = (int*)realloc(arr, capacity * sizeof(int)); } if (arr) { arr[size++] = i; } } free(arr); } int main() { clock_t start, end; start = clock(); inefficientGrowth(); end = clock(); printf("Inefficient growth: %ld ticks\n", end - start); start = clock(); efficientGrowth(); end = clock(); printf("Efficient growth: %ld ticks\n", end - start); return 0; }

Debugging Memory Issues

1. Using Valgrind (External Tool)

// Compile with: gcc -g program.c -o program // Run with: valgrind --leak-check=full ./program #include <stdlib.h> int main() { int *leak = (int*)malloc(100 * sizeof(int)); // Forgot to free - Valgrind will report this int *valid = (int*)malloc(50 * sizeof(int)); free(valid); // Properly freed return 0; }

2. Manual Debugging Aids

#include <stdio.h> #include <stdlib.h> #include <string.h> // Debugging macros #define DEBUG_ALLOC #define DEBUG_PRINT(fmt, ...) \ printf("[%s:%d] " fmt "\n", __FILE__, __LINE__, ##__VA_ARGS__) #ifdef DEBUG_ALLOC void* debugMalloc(size_t size, const char *file, int line) { void *ptr = malloc(size); DEBUG_PRINT("malloc(%zu) = %p at %s:%d", size, ptr, file, line); return ptr; } void debugFree(void *ptr, const char *file, int line) { DEBUG_PRINT("free(%p) at %s:%d", ptr, file, line); free(ptr); } #define malloc(size) debugMalloc(size, __FILE__, __LINE__) #define free(ptr) debugFree(ptr, __FILE__, __LINE__) #endif int main() { int *arr = (int*)malloc(5 * sizeof(int)); for (int i = 0; i < 5; i++) { arr[i] = i; } free(arr); return 0; }

Best Practices Summary

Do's and Don'ts

// DO: Check allocation success int *ptr = (int*)malloc(size * sizeof(int)); if (ptr == NULL) { // Handle error } // DON'T: Assume allocation succeeds int *ptr = (int*)malloc(size * sizeof(int)); // Use ptr without checking // DO: Free memory when done free(ptr); // DON'T: Forget to free // memory leak // DO: Set pointer to NULL after freeing ptr = NULL; // DON'T: Use after free // printf("%d\n", *ptr); // Undefined behavior // DO: Use sizeof with type ptr = (int*)malloc(n * sizeof(int)); // DON'T: Hardcode sizes ptr = (int*)malloc(n * 4); // Assumes int is 4 bytes // DO: Use calloc for zero-initialized arrays ptr = (int*)calloc(n, sizeof(int)); // DON'T: Use malloc and memset separately ptr = (int*)malloc(n * sizeof(int)); memset(ptr, 0, n * sizeof(int)); // Less efficient // DO: Free in reverse order of allocation free(ptr3); free(ptr2); free(ptr1); // DON'T: Free in arbitrary order // Can cause fragmentation

Conclusion

Key Takeaways

  1. malloc() - Allocates uninitialized memory
  2. calloc() - Allocates and zero-initializes memory
  3. realloc() - Resizes existing allocations
  4. free() - Releases allocated memory
  5. Always check allocation success
  6. Always free allocated memory
  7. Avoid memory leaks, dangling pointers, double frees
  8. Use appropriate data structures for your needs

Memory Management Rules

  1. Every malloc/calloc/realloc must have a matching free
  2. Free memory in the reverse order of allocation
  3. Set pointers to NULL after freeing
  4. Check for NULL after allocation
  5. Don't access memory after freeing it
  6. Don't free the same memory twice
  7. Use sizeof for type sizes
  8. Consider memory pools for frequent allocations

When to Use Dynamic Allocation

  • Data structures whose size is unknown at compile time
  • Large data that doesn't fit on the stack
  • Data that must persist beyond function scope
  • Flexible arrays that need to grow/shrink
  • Shared data between different parts of program

Dynamic memory allocation is a powerful feature of C that enables flexible and efficient memory usage, but it requires careful management to avoid errors and leaks.

Complete C Programming Guide + Compilers Collection


1. C srand() Function – Understanding Seed Initialization

https://macronepal.com/understanding-the-c-srand-function
Explains how srand() initializes the pseudo-random number generator in C by setting a seed value. Using the same seed produces the same sequence, while time(NULL) gives different results each run.


2. C rand() Function Mechanics and Limitations

https://macronepal.com/c-rand-function-mechanics-and-limitations
Explains how rand() generates pseudo-random numbers between 0 and RAND_MAX, its deterministic nature, and limitations for security use cases.


3. C log() Function

https://macronepal.com/c-log-function-2
Covers natural logarithm calculation using <math.h> and its applications.


4. Mastering Date and Time in C

https://macronepal.com/mastering-date-and-time-in-c
Explains <time.h> functions like time(), clock(), difftime(), and struct tm.


5. Mastering time_t Type in C

https://macronepal.com/mastering-the-c-time_t-type-for-time-management
Explains time representation as seconds since Unix epoch and conversion functions.


6. C exp() Function

https://macronepal.com/c-exp-function-mechanics-and-implementation
Explains exponential function exp(x) and its scientific applications.


7. C log() Function (Alternate Guide)

https://macronepal.com/c-log-function
Comparison of log() and log10() with usage examples.


8. C log10() Function

https://macronepal.com/mastering-the-log10-function-in-c
Explains base-10 logarithm for engineering and scientific applications.


9. C tan() Function

https://macronepal.com/understanding-the-c-tan-function
Explains tangent function and radian-based calculations.


10. Random Numbers in C (Secure vs Predictable)

https://macronepal.com/mastering-c-random-numbers-for-secure-and-predictable-applications
Explains difference between rand() and secure randomness methods.


11. Free Online C Compiler

https://macronepal.com/free-online-c-code-compiler-2
Browser-based compiler for testing C programs instantly.


C Functions, Arguments, Parameters & Flow

Mastering Functions in C – Complete Guide

https://macronepal.com/c/mastering-functions-in-c-a-complete-guide/
Covers function structure, modular programming, and real-world usage.


Function Arguments in C

https://macronepal.com/c-function-arguments/
Explains how arguments are passed and used in function calls.


Function Parameters in C

https://macronepal.com/c-function-parameters/
Explains defining inputs for functions and matching them with arguments.


Function Declarations in C

https://macronepal.com/c-function-declarations-syntax-rules-and-best-practices/
Covers prototypes, syntax rules, and best practices.


Function Calls in C

https://macronepal.com/understanding-function-calls-in-c-syntax-mechanics-and-best-practices/
Explains execution flow and parameter handling during function calls.


Void Functions in C

https://macronepal.com/understanding-void-functions-in-c-syntax-patterns-and-best-practices/
Explains functions that do not return values.


Return Values in C

https://macronepal.com/c-return-values-mechanics-types-and-best-practices/
Explains different return types and how functions return results.


Pass-by-Value in C

https://macronepal.com/aws/understanding-pass-by-value-in-c-mechanics-implications-and-best-practices/
Explains how copies of variables are passed into functions.


Pass-by-Reference in C

https://macronepal.com/c/understanding-pass-by-reference-in-c-pointers-semantics-and-safe-practices/
Explains using pointers to modify original variables.


C strstr() Function

https://macronepal.com/aws/c-strstr-function/
Explains substring search inside strings in C.


C Preprocessor & Macros

https://macronepal.com/mastering-c-variadic-macros-for-flexible-debugging/
https://macronepal.com/mastering-the-stdc-macro-in-c/
https://macronepal.com/c-time-macro-mechanics-and-usage/
https://macronepal.com/understanding-the-c-date-macro/
https://macronepal.com/c-file-type/
https://macronepal.com/mastering-c-line-macro-for-debugging-and-diagnostics/
https://macronepal.com/mastering-predefined-macros-in-c/
https://macronepal.com/c-error-directive-mechanics-and-usage/
https://macronepal.com/understanding-the-c-pragma-directive/
https://macronepal.com/c-include-directive/


C Structures, Memory, Scope & Linkage

https://macronepal.com/mastering-structures-in-c/
https://macronepal.com/c-structure-declaration-mechanics-and-usage/
https://macronepal.com/c-structure-initialization-mechanics-and-best-practices/
https://macronepal.com/mastering-c-structure-member-access-for-reliable-data-handling/
https://macronepal.com/c-nested-structures/
https://macronepal.com/mastering-arrays-of-structures-in-c/
https://macronepal.com/c-structure-pointers-mechanics-and-implementation/
https://macronepal.com/understanding-c-structure-parameter-passing-mechanics/
https://macronepal.com/mastering-c-returning-structures-for-efficient-data-flow/
https://macronepal.com/c-self-referential-structures/
https://macronepal.com/mastering-structure-alignment-in-c/
https://macronepal.com/c-structure-padding-mechanics-and-optimization/
https://macronepal.com/understanding-c-flexible-array-members-mechanics-and-usage/
https://macronepal.com/mastering-c-anonymous-structures-for-flattened-data-layouts/
https://macronepal.com/c-unions/
https://macronepal.com/mastering-c-name-mangling-and-symbol-decoration/
https://macronepal.com/c-no-linkage-mechanics-and-scope-isolation/
https://macronepal.com/understanding-c-internal-linkage-mechanics-and-architecture/


C Scope, Storage Classes & Typedef

https://macronepal.com/mastering-function-prototype-scope-in-c/
https://macronepal.com/c-function-scope-mechanics-and-visibility/
https://macronepal.com/understanding-c-file-scope-mechanics-and-architecture/
https://macronepal.com/mastering-c-scope-rules-for-predictable-name-resolution/
https://macronepal.com/c-scope-rules/
https://macronepal.com/mastering-c-register-storage-class-for-historical-context-and-modern-alternatives/
https://macronepal.com/mastering-_thread_local-in-c/
https://macronepal.com/c-extern-storage-class-mechanics-and-usage/
https://macronepal.com/understanding-the-c-static-storage-class-mechanics-and-usage/
https://macronepal.com/c-auto-storage-class/
https://macronepal.com/c-typedef-with-pointers/


Extra Articles

https://macronepal.com/13757-2/
https://macronepal.com/13748-2/
https://macronepal.com/13747-2/
https://macronepal.com/13746-2/
https://macronepal.com/13745-2/
https://macronepal.com/13708-2/
https://macronepal.com/13707-2/
https://macronepal.com/13702-2/


Online Compilers

https://macronepal.com/free-html-online-code-compiler/
https://macronepal.com/free-online-python-code-compiler/
https://macronepal.com/free-online-python2-code-compiler/
https://macronepal.com/free-online-java-code-compiler/
https://macronepal.com/free-online-javascript-code-compiler/
https://macronepal.com/free-online-node-js-code-compiler/
https://macronepal.com/free-online-c-code-compiler/
https://macronepal.com/free-online-c-code-compiler-2/
https://macronepal.com/free-online-c-code-compiler-3/
https://macronepal.com/free-online-php-code-compiler/
https://macronepal.com/free-online-ruby-code-compiler/
https://macronepal.com/free-online-perl-code-compiler/
https://macronepal.com/free-online-lua-code-compiler/
https://macronepal.com/free-online-tcl-code-compiler/
https://macronepal.com/free-online-groovy-code-compiler/
https://macronepal.com/free-online-j-shell-code-compiler/
https://macronepal.com/free-online-haskell-code-compiler/
https://macronepal.com/free-online-scala-code-compiler/
https://macronepal.com/free-online-common-lisp-code-compiler/
https://macronepal.com/free-online-d-code-compiler/
https://macronepal.com/free-online-ada-code-compiler/
https://macronepal.com/free-erlang-code-compiler/
https://macronepal.com/free-online-assembly-code-compiler/

Leave a Reply

Your email address will not be published. Required fields are marked *


Macro Nepal Helper