Pointers & Memory
Dynamic Memory Allocation
Ask for memory at run time with malloc, calloc and realloc, give it back with free, and avoid leaks and dangling pointers.
So far every array had a size fixed when you wrote the program. But often you only learn the size while the program runs — how many students in this class? How many lines in this file? Dynamic memory allocation asks the operating system for memory at run time, from a region called the heap.
Stack vs heap
| Stack | Heap | |
|---|---|---|
| Used for | local variables, function calls | memory you request with malloc |
| Size | small (often 1–8 MB) | large (most of RAM) |
| Freed | automatically when the function returns | only when you call free |
| Speed | very fast | slower to allocate |
malloc
malloc(bytes) returns a pointer to a block of uninitialised memory, or NULL if it fails. All four functions live in stdlib.h.
#include <stdio.h>
#include <stdlib.h>
int main(void) {
int n;
printf("Number of students: ");
scanf("%d", &n);
int *marks = malloc(n * sizeof(int)); // room for n ints
if (marks == NULL) {
printf("Out of memory!\n");
return 1;
}
printf("Enter %d marks: ", n);
for (int i = 0; i < n; i++) scanf("%d", &marks[i]); // use it like an array
int total = 0;
for (int i = 0; i < n; i++) total += marks[i];
printf("Average: %.1f\n", (double)total / n);
free(marks); // give the memory back
marks = NULL; // avoid a dangling pointer
return 0;
}Number of students: 5 Enter 5 marks: 80 92 67 74 88 Average: 80.2
calloc
calloc(count, size) allocates count × size bytes and sets them all to zero:
#include <stdio.h>
#include <stdlib.h>
int main(void) {
int *freq = calloc(10, sizeof(int)); // ten zeros
int digits[] = {3, 1, 4, 1, 5, 9, 2, 6, 5, 3, 5};
for (int i = 0; i < 11; i++) freq[digits[i]]++;
for (int d = 0; d < 10; d++)
if (freq[d]) printf("digit %d appears %d time(s)\n", d, freq[d]);
free(freq);
return 0;
}digit 1 appears 2 time(s) digit 2 appears 1 time(s) digit 3 appears 2 time(s) digit 4 appears 1 time(s) digit 5 appears 3 time(s) digit 6 appears 1 time(s) digit 9 appears 1 time(s)
realloc
realloc(ptr, newBytes) resizes a block. It may move the data to a new address, so always use the pointer it returns. This is how growable arrays (like C++'s vector or Python's list) work underneath:
#include <stdio.h>
#include <stdlib.h>
int main(void) {
int capacity = 2, size = 0;
int *a = malloc(capacity * sizeof(int));
for (int value = 1; value <= 9; value++) {
if (size == capacity) {
capacity *= 2; // double when full
int *bigger = realloc(a, capacity * sizeof(int));
if (!bigger) { free(a); return 1; }
a = bigger;
printf("grew to capacity %d\n", capacity);
}
a[size++] = value * value;
}
for (int i = 0; i < size; i++) printf("%d ", a[i]);
printf("\n");
free(a);
return 0;
}grew to capacity 4 grew to capacity 8 grew to capacity 16 1 4 9 16 25 36 49 64 81
malloc vs calloc
malloc(n * size) |
calloc(n, size) |
|---|---|
| one argument: total bytes | two arguments: count and element size |
| memory contains garbage | memory is zeroed |
| slightly faster | safe default when you need zeros |
Dynamic 2D arrays
Allocate an array of row pointers, then each row:
#include <stdio.h>
#include <stdlib.h>
int main(void) {
int rows = 3, cols = 4;
int **grid = malloc(rows * sizeof(int *));
for (int i = 0; i < rows; i++) {
grid[i] = malloc(cols * sizeof(int));
for (int j = 0; j < cols; j++) grid[i][j] = i * cols + j;
}
for (int i = 0; i < rows; i++) {
for (int j = 0; j < cols; j++) printf("%3d", grid[i][j]);
printf("\n");
}
for (int i = 0; i < rows; i++) free(grid[i]); // free rows first
free(grid); // then the row array
return 0;
}0 1 2 3 4 5 6 7 8 9 10 11
Memory leaks
A memory leak happens when you lose the last pointer to a block without freeing it. The memory stays reserved until the program ends. In long-running programs (servers, games) leaks slowly eat all RAM.
void leaky(void) {
int *p = malloc(100 * sizeof(int));
// ... use p ...
// forgot free(p): when the function returns, p disappears but the block stays
}Test yourself
4 questions · pick an answer to check it instantly
- 1
What does
mallocreturn when it cannot allocate memory?Always check the pointer againstNULLbefore using it. - 2
Which function returns zero-filled memory?
callocinitialises every byte to zero;mallocleaves garbage. - 3
After
free(p);, what isp?freedoes not changep. Set it toNULLyourself to avoid accidental reuse. - 4
Why must you use the return value of
realloc?If the block cannot grow in place,realloccopies the data elsewhere and returns the new address.