← Back to C series
🔥
Deep
malloc · free · calloc · realloc

Chapter 2 — Dynamic Memory

`malloc`, `calloc`, `realloc`, `free` — runtime allocation on the **heap**. The boundary between intermediate and "C programmer".

mallocfreerealloc
Duration
1-2 hours
Level
📊 Advanced
Prerequisite
🎯 Deep 1
Outcome
Allocate, use, and free heap memory safely

What you'll learn

  • 1Allocate, use, and `free` heap memory.
  • 2Choose between `malloc`, `calloc`, and `realloc`.
  • 3Detect leaks and double frees mentally before they happen.
  • 4Build a dynamic array.

Core Concepts

1) `malloc` + `free`

c
int *p = malloc(10 * sizeof(int));
if (p == NULL) { /* allocation failed */ }
for (int i = 0; i < 10; i++) p[i] = i;
free(p);
p = NULL;   // optional but defensive

`malloc` does **not** initialize; values are garbage.

2) `calloc`

c
int *p = calloc(10, sizeof(int));   // zero-initialized

`calloc(n, size)` is roughly `malloc(n * size)` + `memset` to 0.

3) `realloc`

c
p = realloc(p, 20 * sizeof(int));   // grow to 20

May return a different pointer; **never** ignore the return value:

c
int *tmp = realloc(p, 20 * sizeof(int));
if (!tmp) { free(p); /* handle */ }
p = tmp;

4) Stack vs heap

StackHeap
Allocated bycompiler`malloc`/`calloc`
Freed byscope exitmanual `free`
Sizesmall, fastbigger, slower
Lifetimeuntil function returnsuntil you `free`

5) Memory bugs to dread

BugCause
Leak`malloc` without `free`
Double free`free` called twice
Use-after-freedereferencing after `free`
Off-by-onewriting one past the end
Uninitialized readusing `malloc`'d memory before writing

Tool: `valgrind ./program` finds most of these.

Examples

Example 1 — `ex01_malloc.c`: basic alloc + use + free

c
int *p = malloc(5 * sizeof(int));
for (int i = 0; i < 5; i++) p[i] = i * 10;
for (int i = 0; i < 5; i++) printf("%d ", p[i]);
free(p);

**Output**

text
0 10 20 30 40

Key: `malloc` gives raw bytes; you fill them yourself.

Example 2 — `ex02_calloc.c`: zero-initialized

c
int *p = calloc(5, sizeof(int));
for (int i = 0; i < 5; i++) printf("%d ", p[i]);
free(p);

**Output**

text
0 0 0 0 0

Key: use `calloc` when zero is the right starting value (most of the time).

Example 3 — `ex03_realloc.c`: grow a dynamic array

c
int *p = malloc(3 * sizeof(int));
p[0] = 1; p[1] = 2; p[2] = 3;
int *tmp = realloc(p, 5 * sizeof(int));
if (!tmp) { free(p); return 1; }
p = tmp;
p[3] = 4; p[4] = 5;
for (int i = 0; i < 5; i++) printf("%d ", p[i]);
free(p);

**Output**

text
1 2 3 4 5

Key: keep the result of `realloc` in a temp pointer to handle failure cleanly.

Example 4 — `ex04_2d.c`: dynamic 2D array

c
int rows = 3, cols = 4;
int **m = malloc(rows * sizeof(int*));
for (int i = 0; i < rows; i++) m[i] = malloc(cols * sizeof(int));
for (int i = 0; i < rows; i++)
    for (int j = 0; j < cols; j++) m[i][j] = i * cols + j;
for (int i = 0; i < rows; i++) {
    for (int j = 0; j < cols; j++) printf("%2d ", m[i][j]);
    printf("\n");
}
for (int i = 0; i < rows; i++) free(m[i]);
free(m);

**Output**

text
 0  1  2  3
 4  5  6  7
 8  9 10 11

Key: free in the reverse order of allocation.

Common mistakes

  1. **Not checking `malloc` return** — NULL deref crashes.
  2. **Forgetting `free`** — leaks add up.
  3. **`free` then deref** — undefined behavior.
  4. **Double free** — corrupts the allocator's bookkeeping.
  5. **Mismatched alloc/free** — `free` only what `malloc/calloc/realloc` returned.

Recap

  • `malloc` if you'll fill it yourself; `calloc` for zero-init; `realloc` to grow.
  • Every alloc must have exactly one matching `free`.
  • Run under `valgrind` while learning.

Try it

bash
cd src
gcc -std=c11 -Wall -o ex01 ex01_malloc.c && ./ex01
gcc -std=c11 -Wall -o ex02 ex02_calloc.c && ./ex02
gcc -std=c11 -Wall -o ex03 ex03_realloc.c && ./ex03
gcc -std=c11 -Wall -o ex04 ex04_2d.c && ./ex04

💻 Examples

Compilable, runnable examples — see the output yourself.

ex01_malloc.cbasic alloc + use + free
CODE
#include <stdio.h>
#include <stdlib.h>

int main(void) {
    int n;
    printf("Array size: ");
    scanf("%d", &n);

    int *arr = malloc((size_t)n * sizeof(int));
    if (arr == NULL) { perror("malloc"); return 1; }

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

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

    free(arr);
    return 0;
}
▶ Output
0 10 20 30 40
ex02_calloc.czero-initialized
CODE
#include <stdio.h>
#include <stdlib.h>

int main(void) {
    size_t n = 5;
    int *arr = calloc(n, sizeof(int));   /* 0  */
    if (!arr) { perror("calloc"); return 1; }

    printf("calloc : ");
    for (size_t i = 0; i < n; i++) printf("%d ", arr[i]);
    putchar('\n');

    free(arr);
    return 0;
}
▶ Output
0 0 0 0 0
ex03_realloc.cgrow a dynamic array
CODE
#include <stdio.h>
#include <stdlib.h>

int main(void) {
    size_t cap = 4;
    size_t len = 0;
    int *arr = malloc(cap * sizeof(int));
    if (!arr) { perror("malloc"); return 1; }

    /* 10     */
    for (int i = 0; i < 10; i++) {
        if (len == cap) {
            cap *= 2;
            int *tmp = realloc(arr, cap * sizeof(int));
            if (!tmp) { free(arr); perror("realloc"); return 1; }
            arr = tmp;
            printf("(capacity grew: %zu)\n", cap);
        }
        arr[len++] = (i + 1) * 100;
    }

    printf("final array: ");
    for (size_t i = 0; i < len; i++) printf("%d ", arr[i]);
    putchar('\n');

    free(arr);
    return 0;
}
▶ Output
1 2 3 4 5
ex04_2d.cdynamic 2D array
CODE
#include <stdio.h>
#include <stdlib.h>

int main(void) {
    int rows = 3, cols = 4;

    /*     */
    int **mat = malloc((size_t)rows * sizeof(int *));
    if (!mat) { perror("malloc"); return 1; }

    /*    */
    for (int i = 0; i < rows; i++) {
        mat[i] = malloc((size_t)cols * sizeof(int));
        if (!mat[i]) { perror("malloc"); return 1; }
        for (int j = 0; j < cols; j++) mat[i][j] = i * cols + j + 1;
    }

    /*  */
    for (int i = 0; i < rows; i++) {
        for (int j = 0; j < cols; j++) printf("%4d", mat[i][j]);
        putchar('\n');
    }

    /*   */
    for (int i = 0; i < rows; i++) free(mat[i]);
    free(mat);
    return 0;
}
▶ Output
 0  1  2  3
 4  5  6  7
 8  9 10 11

📝 Exercises

Try them yourself first, then open the solution to compare.

Exercise 1

Problem 1 (hw01.c)

Goal: Allocate an `int[N]` with `malloc`, fill with 1..N, sum it, free.

Requirements
  • Filename: hw01.c
Toggle solution
SOLUTION
#include <stdio.h>
#include <stdlib.h>

int main(void) {
    int n;
    printf("Enter N: ");
    scanf("%d", &n);
    if (n <= 0) { printf("N must be positive.\n"); return 1; }

    int *arr = malloc((size_t)n * sizeof(int));
    if (!arr) { perror("malloc"); return 1; }

    printf("Enter %d ints: ", n);
    for (int i = 0; i < n; i++) scanf("%d", &arr[i]);

    int mx = arr[0], mn = arr[0];
    for (int i = 1; i < n; i++) {
        if (arr[i] > mx) mx = arr[i];
        if (arr[i] < mn) mn = arr[i];
    }
    printf("max: %d\nmin: %d\n", mx, mn);

    free(arr);
    return 0;
}
Exercise 2

Problem 2 (hw02.c)

Goal: Write `char *strdup_mine(const char *s)` that allocates and copies a string.

Requirements
  • Filename: hw02.c
Toggle solution
SOLUTION
#include <stdio.h>
#include <stdlib.h>

int main(void) {
    int n, m;
    printf("Rows N, cols M: ");
    scanf("%d %d", &n, &m);

    int **mat = calloc((size_t)n, sizeof(int *));
    if (!mat) { perror("calloc"); return 1; }
    for (int i = 0; i < n; i++) {
        mat[i] = calloc((size_t)m, sizeof(int));
        if (!mat[i]) { perror("calloc"); return 1; }
    }

    int len = (n < m) ? n : m;
    for (int i = 0; i < len; i++) mat[i][i] = 1;

    for (int i = 0; i < n; i++) {
        for (int j = 0; j < m; j++) printf("%2d", mat[i][j]);
        putchar('\n');
    }

    for (int i = 0; i < n; i++) free(mat[i]);
    free(mat);
    return 0;
}
Exercise 3

Problem 3 (hw03.c)

Goal: Read a matrix (rows × cols) into a dynamically allocated 2D array, then print its transpose.

Requirements
  • Filename: hw03.c
Toggle solution
SOLUTION
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

char *my_strdup(const char *s) {
    size_t len = strlen(s);
    char *copy = malloc(len + 1);
    if (!copy) return NULL;
    memcpy(copy, s, len + 1);   /*    */
    return copy;
}

int main(void) {
    char buf[256];
    printf("Sentence: ");
    if (fgets(buf, sizeof(buf), stdin) == NULL) return 1;
    buf[strcspn(buf, "\n")] = '\0';

    char *dup = my_strdup(buf);
    if (!dup) { perror("malloc"); return 1; }

    printf("original: %s\n", buf);
    printf("copy:    %s\n", dup);

    free(dup);
    return 0;
}
Example code / lecture materials

All lecture materials and example code are openly available on GitHub.

View on GitHub ↗