Dynamic Memory Management

Chapter 15: Dynamic Memory Management

Dynamic memory management allows your program to request memory from the Heap at runtime. This is necessary when you don't know the amount of data in advance or when you need data to persist beyond the function that created it.

I. The Heap vs. The Stack

  • Stack: Fast, automatic management, small size.
  • Heap: Large, manual management, potential for leaks.

The Stackint x = 10;Automatic allocationDeleted after functionThe Heapint *ptr = malloc(...);Manual allocationPersists until freed

II. Allocation Functions (<stdlib.h>)

1. malloc(size_t size)

Allocates a block of memory of the specified size in bytes. The memory contains garbage values (it is not cleared).

int *arr = malloc(5 * sizeof(int)); // Array for 5 integers
if (arr == NULL) { /* Out of memory handle */ }

2. calloc(size_t num, size_t size)

Allocates memory for num elements of size bytes each. The memory is zeroed out.

3. realloc(void *ptr, size_t new_size)

Resizes a previously allocated memory block. It may move the block to a new address if necessary.

III. Freeing Memory (free)

When you are done with heap memory, you must return it to the system.

free(arr);
arr = NULL; // Safety: prevent dangling pointer

IV. Memory Leaks and Overflows

  1. Memory Leak: Forgetting to call free. Over time, your program consumes more and more RAM until the system crashes.
  2. Double Free: Calling free on the same pointer twice. This causes immediate crashes.
  3. Heap Overflow: Writing beyond the allocated block. This corrupts the heap and lead to security vulnerabilities.

V. Analyzing with Valgrind

Valgrind is the industry standard for detecting memory errors in C programs.

# Run program with leak checking
valgrind --leak-check=full ./my_program

VI. Best Practices

  • Check for NULL: Every call to malloc can fail. Always check if the returned pointer is NULL.
  • One Malloc, One Free: Aim for a clear design where it's obvious who "owns" the memory and is responsible for freeing it.
  • Use Calloc by Default: Unless you are in a performance-critical tight loop, calloc is safer because it prevents reading garbage data.