When the size or lifetime of data isn’t known at compile time, request memory from the heap at run time. Every successful allocation must be released with exactly one free.
Function (<stdlib.h>) | Does | Contents | On failure |
|---|---|---|---|
malloc(size) | Allocate size bytes | Uninitialised (garbage) | NULL |
calloc(n, size) | Allocate n × size bytes (overflow-checked) | All bits zero | NULL |
realloc(p, size) | Grow/shrink a block; may move it | Old contents kept, new part uninitialised | NULL, old block still valid |
free(p) | Release a block | — | free(NULL) is a no-op |
aligned_alloc(al, size) (C11) | Allocate with a given alignment | Uninitialised | NULL |
malloc and free
#include <stdio.h>
#include <stdlib.h>
int main(void) {
int n = 5;
int *a = malloc(n * sizeof *a); /* sizeof *a: stays right if the type changes */
if (a == NULL) { /* always check */
fprintf(stderr, "out of memory\n");
return 1;
}
for (int i = 0; i < n; i++) a[i] = i * i;
for (int i = 0; i < n; i++) printf("%d ", a[i]);
printf("\n");
free(a);
a = NULL; /* defensive: no dangling pointer */
return 0;
}0 1 4 9 16calloc: zeroed memory
#include <stdio.h>
#include <stdlib.h>
int main(void) {
size_t n = 4;
double *v = calloc(n, sizeof *v); /* all zeros, and n * size cannot overflow silently */
if (!v) return 1;
v[2] = 3.5;
for (size_t i = 0; i < n; i++) printf("%.1f ", v[i]);
printf("\n");
free(v);
return 0;
}0.0 0.0 3.5 0.0realloc: a growable array
#include <stdio.h>
#include <stdlib.h>
typedef struct {
int *data;
size_t len, cap;
} Vec;
int vec_push(Vec *v, int x) {
if (v->len == v->cap) {
size_t new_cap = v->cap ? v->cap * 2 : 1; /* geometric growth: amortised O(1) */
int *tmp = realloc(v->data, new_cap * sizeof *tmp);
if (!tmp) return -1; /* v->data is still valid */
v->data = tmp;
v->cap = new_cap;
printf("grew to %zu\n", new_cap);
}
v->data[v->len++] = x;
return 0;
}
int main(void) {
Vec v = {NULL, 0, 0}; /* realloc(NULL, n) acts like malloc(n) */
for (int i = 1; i <= 5; i++) vec_push(&v, i * 10);
printf("len=%zu cap=%zu last=%d\n", v.len, v.cap, v.data[v.len - 1]);
free(v.data);
return 0;
}grew to 1
grew to 2
grew to 4
grew to 8
len=5 cap=8 last=50Allocating strings and structs
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct { char *name; int age; } Person;
char *dup_string(const char *s) { /* like POSIX / C23 strdup */
size_t n = strlen(s) + 1; /* +1 for the terminator! */
char *copy = malloc(n);
if (copy) memcpy(copy, s, n);
return copy;
}
Person *person_new(const char *name, int age) {
Person *p = malloc(sizeof *p);
if (!p) return NULL;
p->name = dup_string(name);
if (!p->name) { free(p); return NULL; }
p->age = age;
return p;
}
void person_free(Person *p) {
if (!p) return;
free(p->name); /* inner allocations first */
free(p);
}
int main(void) {
Person *p = person_new("Ada", 36);
if (!p) return 1;
printf("%s is %d\n", p->name, p->age);
person_free(p);
return 0;
}Ada is 36Rules of ownership
Every
malloc/calloc/reallocsuccess is matched by exactly onefreeof the same pointer.Only pass
freea pointer that came from an allocation function (not a stack array, notp + 1).Decide and document who owns each allocation — the function that creates it, or the caller.
Free nested allocations before the struct that points to them.
Check sizes for overflow:
n * sizecan wrap;callocchecks for you.Leak checkers: Valgrind (
valgrind --leak-check=full ./app), LeakSanitizer (-fsanitize=addresson Linux).
What is the difference between malloc and calloc?
What happens if you call free twice on the same pointer?
Does free return memory to the operating system?
Should you cast the result of malloc in C?