Dynamic Memory — malloc, calloc, realloc, free
0%
C Programming  ·  Dynamic Memory

Dynamic Memory
malloc, calloc, realloc, free

Normal variables have a fixed size decided at compile time. Dynamic memory lets your program ask the OS for exactly as much memory as it needs — while it is running.

Stack vs Heap
malloc()
calloc()
realloc()
free()
NULL check
Memory leaks
5 programs
§1

The Problem malloc() Solves

Why it exists

When you write int marks[100]; you are telling the compiler: "reserve space for exactly 100 integers before the program even starts". But what if the user needs only 5? You wasted 95 slots. What if they need 200? You crash.

Dynamic memory allocation solves this. Instead of deciding size at compile time, you ask for memory at runtime — after the user has told you how much they need.

❌ Fixed size (normal array)
size decided when you write the code
fixed.c
int marks[100];
/* Always 100 slots.
   User enters 5 → 95 wasted.
   User needs 200 → crash! */
✓ Dynamic (malloc)
size decided while program runs
dynamic.c
int n;
scanf("%d", &n);
int *marks = malloc(n * sizeof(int));
/* Exactly n slots.
   No waste. No crash. */

Hotel analogy (your field): A fixed array is like building 100 hotel rooms before any guests arrive. malloc() is like building a room only when a guest books — exactly as many as you need, no more, no less.

stack vs heap
§2

Stack vs Heap — Where Memory Lives

Memory layout

Your program's memory is divided into regions. You need to know two of them:

  • Stack — where normal variables live. Fast. Automatically managed. Limited size. Disappears when function returns.
  • Heap — where malloc() allocates. Larger. You control it. Stays alive until you call free().
Program memory layout — bottom to top
📦 STACKint x; char name[20]; function variables · auto-managed · ~1–8 MB limit
↕ grows toward each other
🏗️ HEAPmalloc() memory lives here · you manage it · can be GBs · grows upward
📋 DATA SEGMENTglobal variables · static variables · initialised at program start
📄 CODE SEGMENTyour compiled program instructions · read-only
FeatureStack (normal variables)Heap (malloc)
Where declaredInside functions normallyAnywhere via malloc()
Size limit~1–8 MB (small)Limited only by RAM (GBs)
When freedAutomatically when function returnsOnly when YOU call free()
SpeedVery fastSlightly slower
RiskStack overflow if too largeMemory leak if you forget free()
Exampleint arr[100];malloc(100 * sizeof(int));
malloc
§3

malloc() — Allocate Raw Memory

Most important

malloc stands for Memory ALLOCation. You tell it how many bytes you need. It finds that space on the heap and gives you back the starting address. You store that address in a pointer.

The memory it gives you is uninitialised — it contains random garbage values until you write to it.

Syntax
pointer = (data_type *) malloc( number_of_bytes );

/* Always use sizeof() — never hardcode byte counts */
int   *p = (int *)   malloc( sizeof(int) );          /* 1 int */
float *f = (float *) malloc( sizeof(float) );         /* 1 float */
int   *a = (int *)   malloc( 5 * sizeof(int) );       /* 5 ints */
char  *s = (char *)  malloc( 50 * sizeof(char) );     /* 50 chars */
How malloc(sizeof(int)) works step by step
Step 1
You call malloc(4) — asking for 4 bytes
Step 2
OS finds 4 free bytes on the heap at address 5000
Step 3
malloc() returns 5000 (the address)
Step 4
You store it: int *p = 5000
Step 5
*p = 42 — writes 42 into that memory
heap[5000] = 42
malloc_basic.c
C
#include <stdio.h>
#include <stdlib.h>    /* malloc and free live here */

int main() {
    /* Allocate memory for one integer on the heap */
    int *p = (int *) malloc(sizeof(int));

    /* ALWAYS check — malloc returns NULL if it fails */
    if (p == NULL) {
        printf("Memory allocation failed!\n");
        return 1;
    }

    *p = 500;    /* store value in heap memory */
    printf("Value = %d\n", *p);
    printf("Address = %p\n", p);

    free(p);     /* ALWAYS free when done — return memory to OS */
    p = NULL;    /* good habit — avoid using freed pointer */

    return 0;
}
output
Value = 500
Address = 0x55a3f2c (example heap address)
Always check for NULL. If the system is out of memory, malloc() returns NULL. Dereferencing NULL crashes the program. One if (p == NULL) check prevents this.
dynamic array
§4

Dynamic Array — User Decides the Size

Most useful use case

The most common use of malloc() is creating an array whose size is decided by the user at runtime. You allocate n * sizeof(type) bytes — enough room for n elements — and use the pointer exactly like a normal array.

dynamic_array.c
C
#include <stdio.h>
#include <stdlib.h>

int main() {
    int  n, i;
    int *marks;

    printf("How many students? ");
    scanf("%d", &n);

    /* Allocate exactly n integers — decided at runtime */
    marks = (int *) malloc(n * sizeof(int));

    if (marks == NULL) {
        printf("Not enough memory!\n");
        return 1;
    }

    /* Use marks[] exactly like a normal array */
    printf("Enter %d marks:\n", n);
    for (i = 0; i < n; i++) {
        printf("  Student %d: ", i + 1);
        scanf("%d", &marks[i]);
    }

    /* Compute total and average */
    int   total = 0;
    for (i = 0; i < n; i++) total += marks[i];
    printf("Total   : %d\n", total);
    printf("Average : %.1f\n", (float)total / n);

    free(marks);    /* release heap memory */
    marks = NULL;

    return 0;
}
output — user enters 3
How many students? 3
Enter 3 marks:
  Student 1: 85
  Student 2: 92
  Student 3: 78
Total   : 255
Average : 85.0
marks[i] works exactly like a normal array. Once you have the pointer, marks[0], marks[1], etc. access elements just like a regular array. The pointer and the array notation are the same thing — marks[i] is *(marks + i).
calloc
§5

calloc() — Allocate and Zero-Fill

Clean memory

calloc stands for Contiguous ALLOCation. It does the same job as malloc() — allocates heap memory — but with two differences:

  • Different syntax — takes two arguments: number of elements and size of each
  • Zero-fills — every byte is set to 0 automatically. malloc() leaves garbage, calloc() gives you clean zeros.
Syntax — calloc vs malloc
/* malloc — one argument — total bytes — garbage values */
int *a = (int *) malloc( 5 * sizeof(int) );

/* calloc — two arguments — count, size — zeros everything */
int *b = (int *) calloc( 5, sizeof(int) );

/* Both give you space for 5 integers.
   calloc initialises all to 0.
   malloc leaves random values. */
calloc_vs_malloc.c
C
#include <stdio.h>
#include <stdlib.h>

int main() {
    int i;

    /* malloc — values are garbage (unknown) */
    int *a = (int *) malloc(5 * sizeof(int));
    printf("malloc (garbage): ");
    for (i = 0; i < 5; i++) printf("%d ", a[i]);

    /* calloc — values are always 0 */
    int *b = (int *) calloc(5, sizeof(int));
    printf("\ncalloc (zeros):   ");
    for (i = 0; i < 5; i++) printf("%d ", b[i]);
    printf("\n");

    free(a); free(b);
    return 0;
}
output
malloc (garbage): 13248 0 -274829 1024 88  (random junk)
calloc (zeros):   0 0 0 0 0
Use calloc() when you want all values to start at 0 — counters, score boards, frequency tables. Use malloc() when you will fill all values yourself right away (no need to zero first — faster).
realloc
§6

realloc() — Resize Already Allocated Memory

Grow or shrink

After allocating memory with malloc(), you might need more space. realloc() resizes an existing allocation — it tries to extend it in place, or moves it somewhere larger if needed. The data already stored is preserved.

Syntax
pointer = (data_type *) realloc( old_pointer, new_size_in_bytes );

int *p = (int *) malloc(3 * sizeof(int));   /* 3 ints */
p = (int *) realloc(p, 6 * sizeof(int));    /* grow to 6 ints */
/* original 3 values are still there */
realloc_example.c
C
#include <stdio.h>
#include <stdlib.h>

int main() {
    int  i;
    int *p;

    /* Start with 3 integers */
    p = (int *) malloc(3 * sizeof(int));
    p[0] = 10;  p[1] = 20;  p[2] = 30;

    printf("Before realloc: ");
    for (i = 0; i < 3; i++) printf("%d ", p[i]);

    /* Grow to 6 integers — original values preserved */
    p = (int *) realloc(p, 6 * sizeof(int));
    if (p == NULL) { printf("realloc failed!\n"); return 1; }

    p[3] = 40;  p[4] = 50;  p[5] = 60;

    printf("\nAfter  realloc: ");
    for (i = 0; i < 6; i++) printf("%d ", p[i]);
    printf("\n");

    free(p);
    return 0;
}
output
Before realloc: 10 20 30
After  realloc: 10 20 30 40 50 60
Original 10, 20, 30 are still there after realloc. realloc copied them to the new larger block. This is how dynamic arrays in real programs grow — start small, double in size when full, realloc to the bigger size.
free and memory leaks
§7

free() and Memory Leaks — The Rules

Most important habit

Every call to malloc() or calloc() must be matched with a call to free(). If you allocate memory and never free it, it stays reserved for your program but unusable — this is called a memory leak.

A small program leaking a few bytes doesn't matter. But a server program that runs 24/7 and leaks memory every request will eventually use all the RAM and crash the entire system.

❌ Memory Leak
allocated but never freed
leak.c
int *p = malloc(sizeof(int));
*p = 42;
/* forgot free(p) — memory gone */
return 0;  /* leak! */
✓ Correct
every malloc has a matching free
correct.c
int *p = malloc(sizeof(int));
*p = 42;
free(p);     /* ← matched */
p = NULL;    /* safe habit */
return 0;

Four rules to always follow:

  • Rule 1 — Always check if (ptr == NULL) right after malloc. Never skip this.
  • Rule 2 — Every malloc() or calloc() must have exactly one free().
  • Rule 3 — After free(ptr), immediately set ptr = NULL. Using a freed pointer is undefined behaviour.
  • Rule 4 — Never free() the same pointer twice — double-free causes a crash.
Never use a pointer after free(). free(p) releases the memory. If you then write *p = 5, you are writing into memory that now belongs to someone else. This causes random crashes that are very hard to debug.
all four together
§8

Complete Program — All Four Functions Together

Reference program
dynamic_memory_complete.c
C
#include <stdio.h>
#include <stdlib.h>

int main() {
    int  i, n;
    int *p;

    printf("Enter number of students: ");
    scanf("%d", &n);

    /* ── malloc: allocate for n students ────────────── */
    p = (int *) malloc(n * sizeof(int));
    if (p == NULL) {
        printf("malloc failed!\n"); return 1;
    }
    printf("malloc: %d slots allocated.\n", n);

    /* Fill with marks */
    for (i = 0; i < n; i++) p[i] = (50 + i * 5);
    printf("Values: ");
    for (i = 0; i < n; i++) printf("%d ", p[i]);
    printf("\n");

    /* ── realloc: add 3 more students ───────────────── */
    p = (int *) realloc(p, (n + 3) * sizeof(int));
    if (p == NULL) {
        printf("realloc failed!\n"); return 1;
    }
    printf("realloc: grown to %d slots.\n", n + 3);
    p[n] = 95; p[n+1] = 88; p[n+2] = 72;
    n += 3;

    /* ── calloc: a separate zeroed score array ──────── */
    int *bonus = (int *) calloc(n, sizeof(int));
    printf("calloc bonus array (all zeros): ");
    for (i = 0; i < n; i++) printf("%d ", bonus[i]);
    printf("\n");

    /* Final marks + bonus */
    printf("Final marks:  ");
    for (i = 0; i < n; i++) printf("%d ", p[i]);
    printf("\n");

    /* ── free: both allocations ─────────────────────── */
    free(p);     p     = NULL;
    free(bonus); bonus = NULL;
    printf("Memory freed. Done.\n");

    return 0;
}
output — user enters 4
Enter number of students: 4
malloc: 4 slots allocated.
Values: 50 55 60 65
realloc: grown to 7 slots.
calloc bonus array (all zeros): 0 0 0 0 0 0 0
Final marks:  50 55 60 65 95 88 72
Memory freed. Done.
quick reference
§9

Quick Reference — All Four Functions

FunctionPurposeSyntaxInitial values
malloc() Allocate raw memory malloc(n * sizeof(type)) Garbage (random)
calloc() Allocate + zero-fill calloc(n, sizeof(type)) All zeros
realloc() Resize existing allocation realloc(ptr, new_size) Old data preserved
free() Release heap memory free(ptr) Memory returned to OS
Header neededRule
#include <stdlib.h>Required for all four functions
NULL checkAlways check if (ptr == NULL) after malloc/calloc/realloc
Matching freeEvery malloc must have exactly one free
After freeSet ptr = NULL immediately after free(ptr)
sizeof()Always use sizeof() — never hardcode byte counts
quiz
Q

Quick Quiz

Q 1 of 5

What does malloc(5 * sizeof(int)) do?

Q 2 of 5

What is the difference between malloc() and calloc()?

Q 3 of 5

What is a memory leak?

Q 4 of 5

Why must you check if (ptr == NULL) after malloc?

Q 5 of 5

What does realloc(p, 10 * sizeof(int)) do if p already points to 5 ints?

Lesson Checklist

  • Fixed arrays are decided at compile time — dynamic memory is decided at runtime
  • Stack = automatic, small, fast. Heap = manual, large, you manage it.
  • malloc(n * sizeof(type)) — allocates n elements — returns address — values are garbage
  • calloc(n, sizeof(type)) — same but sets all bytes to 0
  • realloc(ptr, new_size) — resizes — old data preserved
  • Always check if (ptr == NULL) after malloc/calloc/realloc
  • Every malloc must be matched with exactly one free()
  • After free(p), set p = NULL immediately
  • Memory leak = allocate but never free — program slowly consumes all RAM
  • I completed the quiz