L16: Dynamic Memory Allocation

Dynamic Memory Allocation #


What is Dynamic Memory Allocation? #

Dynamic memory allocation allows programs to request memory at runtime rather than compile time.

Used when:

  • The amount of data is not known beforehand.
  • Data structures need to grow or shrink during execution.

Why Not Static Allocation? #

Static MemoryDynamic Memory
Size fixed at compile timeSize determined at runtime
Allocated on stackAllocated on heap
Automatically freedMust be freed manually
Limited sizeFlexible and scalable

Functions in <stdlib.h> #

FunctionDescription
malloc(size)Allocates uninitialized memory
calloc(n, size)Allocates and zero-initializes memory
realloc(ptr, size)Changes the size of an existing allocation
free(ptr)Frees allocated memory

🧩 Example 1: Using malloc() #

#include <stdio.h>
#include <stdlib.h>

int main() {
    int n;
    printf("Enter number of integers: ");
    scanf("%d", &n);

    int *arr = malloc(n * sizeof(int));

    if (!arr) {
        printf("Memory allocation failed!\n");
        return 1;
    }

    for (int i = 0; i < n; i++) arr[i] = i * i;

    for (int i = 0; i < n; i++) printf("%d ", arr[i]);

    free(arr);
    return 0;
}

✅ Allocates memory for n integers on the heap.


🧩 Example 2: calloc() for Zero Initialization #

#include <stdio.h>
#include <stdlib.h>

int main() {
    int *arr = calloc(5, sizeof(int));
    for (int i = 0; i < 5; i++) printf("%d ", arr[i]);
    free(arr);
}

calloc() ensures all elements are initialized to 0.


🧩 Example 3: realloc() for Resizing #

#include <stdio.h>
#include <stdlib.h>

int main() {
    int *arr = malloc(3 * sizeof(int));
    for (int i = 0; i < 3; i++) arr[i] = i + 1;

    arr = realloc(arr, 5 * sizeof(int));
    arr[3] = 4; arr[4] = 5;

    for (int i = 0; i < 5; i++) printf("%d ", arr[i]);
    free(arr);
}

✅ Expands or shrinks a previously allocated block.


🔹 Common Use Case 1: Dynamic Arrays #

When user input size is unknown at compile time.

int *arr = malloc(n * sizeof(int));

🔹 Common Use Case 2: Social Networks #

Different people can have different number of friends. So we need something like a 2d array with different rows having different sizes.



#include <stdio.h>
#include <stdlib.h>
#include <string.h>

typedef struct Person {
    char name[10];
    // friends is an array of person pointers
    struct Person** friends; 
    int num_friends;
} Person;

int main() {

  Person* a = malloc(sizeof(Person));
  strcpy(a->name, "Alice");
  
  Person* b = malloc(sizeof(Person));
  strcpy(b->name, "Bob");
  
  Person* c = malloc(sizeof(Person));
  strcpy(c->name, "Charlie");
  
  Person* d = malloc(sizeof(Person));
  strcpy(c->name, "Diestel");
  
  // a has 2 friends: b, c
  a->friends = malloc(2*sizeof(Person*));
  a->friends[0] = b;
  a->friends[1] = c;
  a->num_friends = 2;
  
  // b has 2 friends: c, a
  b->friends = malloc(2*sizeof(Person*));
  b->friends[0] = c;
  b->friends[1] = a;
  b->num_friends = 2;
  
  // c has 1 friend: d
  c->friends = malloc(1*sizeof(Person*));
  c->friends[0] = d;
  c->num_friends = 1;
  
  // d has no friends
  d->friends = NULL;
  d->num_friends = 0;

  return 0;
}

Pythontutor

💡 Used in:

  • Dynamic table storage
  • Graph adjacency matrices
  • Image data buffers

🔹 Common Use Case 3: Linked Lists #

Each node is created dynamically to store data. Data can be stored in different memory locations without needing to be contigous.

#include <stdio.h>
#include <stdlib.h>

typedef struct Node {
    int data;
    struct Node* next;
} Node;

int main() {

    // Linkedlist in Stack
    // Node a, b, c;
    // a.data = 1;
    // a.next = &b;
    // b.data = 2;
    // b.next = &c;
    // c.data = 3;
    // c.next = NULL;
    
    // LinkedList in Heap
    
    Node* a = malloc(sizeof(Node));
    Node* b = malloc(sizeof(Node));
    Node* c = malloc(sizeof(Node));
    a->data = 1;
    a->next = b;
    b->data = 2;
    b->next = c;
    c->data = 3;
    c->next = NULL;
    
    
    return 0;
}

pythontutor


⚠️ Memory Management Best Practices #

✅ Always check if malloc() returned NULL.
✅ Always free() memory after use.
✅ Avoid memory leaks and dangling pointers.
✅ Use tools like Valgrind to detect leaks.


🚫 Common Errors #

ErrorCause
Segmentation faultUsing uninitialized or freed pointer
Memory leakForgetting to call free()
Double freeFreeing the same pointer twice
Buffer overflowWriting beyond allocated memory

🧠 Summary #

FunctionUseKey Point
malloc()Allocate memoryUninitialized memory
calloc()Allocate + zero initializeSlower, safer
realloc()Resize memoryRetains old data
free()Release memoryMust call manually

💡 Exercises #

  1. Implement a dynamically growing array that doubles in size.
  2. Create a linked list of student records using dynamic memory.
  3. Write a function to dynamically allocate a 2D array.
  4. Simulate dynamic allocation of memory for a tree.

🏁 Takeaway #

Dynamic memory makes C powerful — but with great power comes great responsibility.

Always pair every malloc() with a matching free()!


References #