Dynamic memory: malloc, free, and realloc
Stack memory vs heap memory
- Ordinary non-static local variables have automatic storage duration: their lifetime ends when their block finishes. Implementations usually put them on the stack; C does not require a particular memory layout.
- Sometimes you need memory that outlives the function, or whose size you only know at run time.
- That memory comes from the heap. You ask for it, and later you give it back.
malloc and sizeof
mallocasks the heap for a block of bytes and returns a pointer to it.- Use
sizeofto get the size of one item:malloc(n * sizeof(int))requests room fornints. Check forNULLbefore using them; newly allocated values are uninitialised. Here sizes are small and positive, so the byte calculation fits. - The block is yours to use through the pointer, exactly like an array:
a[0],a[1], ...
free and memory leaks
- When you are done with heap memory, give it back with
free(pointer). - If you never
freeit, the program leaks memory — it holds space it no longer uses. - Rule for these tasks: your function allocates and returns; the checker frees it. Do not call
freeyourself.
#include <stdio.h>
#include <stdlib.h>
int main(void) {
int *a = malloc(3 * sizeof(int)); // room for 3 ints
if (a == NULL) return EXIT_FAILURE;
a[0] = 5; a[1] = 6; a[2] = 7;
printf("%d\n", a[0] + a[1] + a[2]); // 18
free(a); // give it back
return 0;
}
Growing an array with realloc
- For a positive byte size,
realloc(a, newSize)preserves the firstmin(oldSize, newSize)bytes when it succeeds. Newly added cells need initialising. - On failure it returns
NULLand leaves the old allocation intact. Save the result in a temporary pointer; overwriting your only pointer withNULLloses access to the old block. - On success use only the returned pointer: the block may have moved. Avoid zero-size
reallochere because its rules differ between C versions.
#include <stdio.h>
#include <stdlib.h>
int main(void) {
int *a = malloc(2 * sizeof *a);
if (a == NULL) return EXIT_FAILURE;
a[0] = 4; a[1] = 9;
int *grown = realloc(a, 3 * sizeof *a);
if (grown == NULL) {
free(a); // the old allocation still belongs to us
return EXIT_FAILURE;
}
a = grown;
a[2] = 16;
printf("%d %d %d\n", a[0], a[1], a[2]); // 4 9 16
free(a);
return 0;
}
Common mistakes
- Every
mallocneeds a matchingfree; do not use memory afterfree. - Check allocation results before dereferencing. A failed growth must not change the recorded length or free the caller's old array.
Now you try
- Use
malloc(n * sizeof(int))for new arrays, and#include <stdlib.h>. - Your function returns the pointer; the checker frees it — do not call
free. Do not write amain.
Difficulty 4/5 · 280 coins + a small bonus on first completion today
Complete int *make_array(int n, int value) so it allocates n ints (with malloc), sets every one to value, and returns the pointer. The checker frees it. Do not write a main. Here 1 ≤ n ≤ 1000. If allocation fails, return NULL without accessing any elements.
Click Run to see the output here.
Difficulty 4/5 · 280 coins + a small bonus on first completion today
Complete int *concat(const int a[], int na, const int b[], int nb) so it allocates na + nb ints, copies all of a then all of b, and returns the pointer. The checker frees it. Do not write a main. Lengths are non-negative and their sum is 1 to 1000. Leave both inputs unchanged; return NULL on allocation failure.
Click Run to see the output here.
Difficulty 4/5 · 280 coins + a small bonus on first completion today
Complete int *push(int *a, int *len, int value) so it grows the heap array a to hold one more int with realloc, stores value at the end, increases *len by 1, and returns the new pointer. The checker frees it. Do not write a main. Here 0 ≤ *len < 1000. On allocation failure return NULL, leaving the old allocation and *len unchanged. The caller keeps its old pointer until it checks your result.
Click Run to see the output here.