Skip to main content
Advanced Data Structures
Advanced Data Structures
IOT
Rx 1h30

Dynamic Memory Allocation

Objectives

  • Understand the difference between stack and heap
  • Master malloc, calloc, realloc, and free
  • Avoid common errors (memory leaks, double free)

Memory Layout

A C program uses several memory regions:

RegionDescriptionCharacteristics
CodeProgram instructionsRead-only
DataGlobal and static variablesFixed size at compile time
StackLocal variablesLIFO, limited size (~8MB)
HeapDynamic allocationLarge size, manually managed
Stack vs Heap
  • Stack: automatic allocation, fast, but limited in size
  • Heap: manual allocation with malloc, slower but practically unlimited

Allocation Functions

malloc - Memory ALLOCation

#include <stdlib.h>

// Allocate n bytes, contents uninitialized
void* malloc(size_t n);

Example:

// Allocate an array of 10 integers
int* tab = (int*)malloc(10 * sizeof(int));
if (tab == NULL) {
    // Allocation failed
    exit(EXIT_FAILURE);
}

calloc - Clear ALLOCation

// Allocate n elements of size size, initialized to 0
void* calloc(size_t n, size_t size);

Example:

// Allocate and initialize to 0
int* tab = (int*)calloc(10, sizeof(int));

realloc - REALLOCation

// Resize a memory block
void* realloc(void* ptr, size_t new_size);

Example:

// Grow the array from 10 to 20 elements
tab = (int*)realloc(tab, 20 * sizeof(int));
Be careful with realloc

realloc may move the memory block. Always use the returned pointer:

int* new_tab = realloc(tab, new_size);
if (new_tab == NULL) {
    // Keep the old tab intact
    free(tab);
    exit(EXIT_FAILURE);
}
tab = new_tab;

free - Deallocation

// Free allocated memory
void free(void* ptr);

Common Errors

1. Memory leak

void memory_leak() {
    int* p = malloc(100 * sizeof(int));
    // ... use of p
    // Forgot to call free(p)!
}  // Memory remains allocated but inaccessible

2. Double free

void double_free() {
    int* p = malloc(sizeof(int));
    free(p);
    free(p);  // ERROR: undefined behavior!
}

3. Use after free

void use_after_free() {
    int* p = malloc(sizeof(int));
    *p = 42;
    free(p);
    printf("%d", *p);  // ERROR: accessing freed memory!
}

4. Buffer overflow

void buffer_overflow() {
    int* tab = malloc(10 * sizeof(int));
    for (int i = 0; i <= 10; i++) {  // <= instead of <
        tab[i] = i;  // ERROR: writing out of bounds!
    }
    free(tab);
}

Best Practices

  1. Always check the return value of malloc/calloc/realloc
  2. Always free allocated memory
  3. Set to NULL after free to avoid use-after-free
  4. Use valgrind to detect leaks
// Safe allocation pattern
int* safe_malloc(size_t n) {
    int* ptr = (int*)malloc(n * sizeof(int));
    if (ptr == NULL) {
        fprintf(stderr, "Allocation error\n");
        exit(EXIT_FAILURE);
    }
    return ptr;
}

// Safe deallocation pattern
void safe_free(int** ptr) {
    if (ptr != NULL && *ptr != NULL) {
        free(*ptr);
        *ptr = NULL;  // Prevents double free
    }
}

Using valgrind

# Compile with -g to get line numbers
gcc -g programme.c -o programme

# Check for memory leaks
valgrind --leak-check=full ./programme

Typical output:

==12345== HEAP SUMMARY:
==12345==     in use at exit: 0 bytes in 0 blocks
==12345==   total heap usage: 5 allocs, 5 frees, 1,024 bytes allocated
==12345== All heap blocks were freed -- no leaks are possible

Summary

FunctionUsageInitialization
malloc(size)Simple allocationNo
calloc(n, size)Array allocationYes (to 0)
realloc(ptr, size)ResizingPreserves existing data
free(ptr)Deallocation-