C course menu
Dynamic Memory Allocation in C: malloc, calloc, realloc and free
Learn dynamic memory in C: stack versus heap, allocating with malloc and calloc, checking for NULL, resizing safely with realloc, releasing memory with free, building a growable array, and allocating strings and 2D arrays.
On this page
So far every variable's size was fixed when you wrote the program. Dynamic memory lets a program request memory at run time, when it knows how much it needs, and keep it for as long as it wants.
Stack and heap#
malloc and free#
malloc(bytes) (from <stdlib.h>) returns a pointer to a new block of uninitialised memory, or NULL if it fails. free(ptr) gives the block back:
#include <stdio.h>
#include <stdlib.h>
int main(void) {
int n = 5;
int *squares = malloc(n * sizeof *squares);
if (squares == NULL) {
fprintf(stderr, "out of memory\n");
return 1;
}
for (int i = 0; i < n; i++) squares[i] = (i + 1) * (i + 1);
for (int i = 0; i < n; i++) printf("%d ", squares[i]);
printf("\n");
free(squares);
squares = NULL; /* avoid using it again by accident */
return 0;
}1 4 9 16 25 n * sizeof *squareskeeps the size correct even if the element type changes later.- Always check for
NULL. - Every successful
mallocneeds exactly onefree.free(NULL)is allowed and does nothing.
calloc#
calloc(count, size) allocates an array and sets every byte to zero:
#include <stdio.h>
#include <stdlib.h>
int main(void) {
int *votes = calloc(4, sizeof *votes);
if (votes == NULL) return 1;
int ballots[] = {2, 0, 2, 3, 2, 1, 0};
for (int i = 0; i < 7; i++) votes[ballots[i]]++;
for (int i = 0; i < 4; i++) printf("candidate %d: %d\n", i, votes[i]);
free(votes);
return 0;
}candidate 0: 2
candidate 1: 1
candidate 2: 3
candidate 3: 1realloc: growing a block#
realloc(ptr, new_size) resizes a block, moving it if necessary and keeping the old contents. If it fails it returns NULL and leaves the old block untouched, so assign the result to a temporary first:
#include <stdio.h>
#include <stdlib.h>
int main(void) {
int capacity = 2, count = 0;
int *items = malloc(capacity * sizeof *items);
if (items == NULL) return 1;
for (int value = 10; value <= 70; value += 10) {
if (count == capacity) {
int new_capacity = capacity * 2;
int *bigger = realloc(items, new_capacity * sizeof *items);
if (bigger == NULL) {
free(items);
return 1;
}
items = bigger;
capacity = new_capacity;
printf("grew to %d\n", capacity);
}
items[count++] = value;
}
for (int i = 0; i < count; i++) printf("%d ", items[i]);
printf("\n");
free(items);
return 0;
}grew to 4
grew to 8
10 20 30 40 50 60 70 items = realloc(items, ...) would lose the only pointer to the old block if realloc failed, leaking it. Doubling the capacity keeps the number of reallocations small; this is how growable arrays such as C++'s std::vector work.
Allocating a string#
strlen does not count the '\0', so allocate one extra byte:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
char *join(const char *a, const char *b) {
size_t len = strlen(a) + strlen(b);
char *out = malloc(len + 1); /* + 1 for '\0' */
if (out == NULL) return NULL;
strcpy(out, a);
strcat(out, b);
return out; /* the caller must free it */
}
int main(void) {
char *s = join("dynamic ", "memory");
if (s == NULL) return 1;
printf("%s (%zu)\n", s, strlen(s));
free(s);
return 0;
}dynamic memory (14)A function that returns allocated memory should say clearly, in its documentation, that the caller owns it and must free it.
A 2D array on the heap#
Allocate one block and compute the index yourself; it needs one malloc and one free:
#include <stdio.h>
#include <stdlib.h>
int main(void) {
int rows = 3, cols = 4;
int *grid = malloc((size_t)rows * cols * sizeof *grid);
if (grid == NULL) return 1;
for (int r = 0; r < rows; r++)
for (int c = 0; c < cols; c++)
grid[r * cols + c] = r * 10 + c;
printf("%d %d\n", grid[1 * cols + 2], grid[2 * cols + 3]);
free(grid);
return 0;
}12 23Practice#
Practice exercise
Write int *range(int start, int end, int *out_len) that allocates and returns an array containing start, start + 1, ..., end - 1, storing the length through out_len. Print range(3, 9, &len) and free it.
Show solution
#include <stdio.h>
#include <stdlib.h>
int *range(int start, int end, int *out_len) {
int n = end > start ? end - start : 0;
*out_len = n;
if (n == 0) return NULL;
int *a = malloc(n * sizeof *a);
if (a == NULL) {
*out_len = 0;
return NULL;
}
for (int i = 0; i < n; i++) a[i] = start + i;
return a;
}
int main(void) {
int len;
int *r = range(3, 9, &len);
for (int i = 0; i < len; i++) printf("%d ", r[i]);
printf("(length %d)\n", len);
free(r);
return 0;
}3 4 5 6 7 8 (length 6)Key takeaway#
Finished this lesson?