Dynamic memory: malloc and free

Intermediate

Introduction

Local variables live on the stack and disappear when their function returns. Sometimes you need memory whose size you learn only while the program runs, or that must outlive a function. For that, C has the heap.

So far, the size of everything has been known before the program runs. Real programs often cannot know that: how many lines does this file have? The heap lets you ask for exactly what you need, at the moment you need it, as long as you remember to give it back.

How it works

Leaks and dangling pointers

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

int main(void) {
    int *data = malloc(2 * sizeof(int));
    data[0] = 1;
    data[1] = 2;
    data = malloc(4 * sizeof(int));
    data[0] = 7;
    free(data);
    data[0] = 8;
    return 0;
}
Open in compiler

When the last pointer to a block is lost, the block can never be freed: a memory leak. StepVioz marks such a block in red. Using a block after freeing it, or freeing it twice, is also an error; real C may crash much later or silently corrupt data, while StepVioz stops on the exact line.

Syntax

int *a = malloc(n * sizeof(int));
if (a == NULL) {
    return 1;
}
int *b = realloc(a, 2 * n * sizeof(int));
free(b);

Example

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

int main(void) {
    int n = 3;
    int *scores = malloc(n * sizeof(int));
    if (scores == NULL) return 1;
    for (int i = 0; i < n; i++) {
        scores[i] = (i + 1) * 10;
    }
    int total = scores[0] + scores[1] + scores[2];
    printf("total %d\n", total);
    free(scores);
    scores = NULL;
    return 0;
}
Open in compiler
  • malloc(n * sizeof(int)) asks for room for n ints and returns its address (or NULL if there is no memory left).
  • The block appears under Heap in the memory panel, with scores pointing to it. Its cells start as ?: malloc does not clear them.
  • free(scores) gives the block back. After that, scores still holds the old address, shown as dangling, so the program sets it to NULL.

calloc(n, size) is like malloc but sets every byte to 0. realloc(p, new_size) makes a block bigger or smaller.

Line by line

  1. n is the number of elements, known only as the program runs.
  2. malloc asks for room for three ints, and returns its address.
  3. The if handles the rare case of no memory being available.
  4. The loop fills the block, which is used just like an array.
  5. free returns the block, and setting scores to NULL removes the dangling address.

Expected result

total 60

In the real world

Browsers, games and servers allocate and release memory constantly, and leaks are a common reason for a long-running program slowing down. Whole languages, such as Rust, were designed to make these mistakes impossible.

Common mistakes

  • Forgetting free: every malloc needs exactly one free.
  • Using memory after free, or calling free twice. Set the pointer to NULL after free.
  • malloc(n) when you meant malloc(n * sizeof(int)): that is only n bytes.
  • Not checking for NULL after malloc.

Best practices

  • Write the free when you write the malloc.
  • Use sizeof with the type, rather than typing a number of bytes.
  • Keep a single clear owner for each block.

Summary

malloc gets memory from the heap at run time, and free returns it. realloc resizes a block. Each block must be freed exactly once, and used only while it is allocated.

Quick check

1. How do you ask for room for 10 doubles?
2. What is a memory leak?
3. After free(p), what should you usually do?

Key takeaways

  • Every malloc needs exactly one free.
  • Check the result of malloc for NULL.
  • Set a pointer to NULL after freeing it.

Practice problems for this lesson

My notes

Write down what you want to remember from this lesson. Notes are saved on this device as you type, and appear on your dashboard.