The heap and malloc
Stack memory is automatic but limited: locals vanish when their function returns, and their sizes are usually fixed when you compile. What if you need memory that outlives a function, or a size you only learn at runtime? You ask the heap.
#include <stdlib.h>
int *nums = malloc(n * sizeof(int)); // room for n ints
if (nums == NULL) {
// out of memory: handle it
}
nums[0] = 42; // use it like an array
free(nums); // give it back
malloc reserves a block of bytes and returns a pointer to it, or NULL if it cannot. Note that it counts bytes, which is why you multiply by sizeof(int). The memory is not zeroed: it holds whatever was there before, so set every element before you read it. (calloc gives you zeroed memory if you need it.)
Heap memory stays yours until you call free on it. That is the deal C makes: total control, total responsibility. Three classic ways to break it:
- Memory leak: you forget to
free. The program keeps holding memory it no longer uses. A short program gets it all back when it exits, but a game, a browser or a server that leaks a little per frame or per request grows until something gives. - Use after free: reading or writing through a pointer after freeing it. Undefined behaviour.
- Double free: freeing the same block twice. Also undefined behaviour.
A common habit is free(p); p = NULL; so a stale pointer cannot be used by accident.
Need the block to grow? realloc(p, new_size) returns a pointer to a bigger block with the old contents copied in. Languages with garbage collectors do all of this for you. Now you know what they are doing behind your back.
Try it yourself
Edit it. Break it. Run it again.#include <stdio.h>
#include <stdlib.h>
int main(void) {
int n = 5;
int *squares = malloc(n * sizeof(int));
if (squares == NULL) {
printf("out of memory\n");
return 1;
}
for (int i = 0; i < n; i++) {
squares[i] = i * i;
}
for (int i = 0; i < n; i++) {
printf("%d ", squares[i]);
}
printf("\nthat block was %zu bytes on the heap\n", n * sizeof(int));
free(squares);
squares = NULL;
return 0;
}
Your turn
Type it yourself. That is the whole trick.1A list sized at runtime
Read n from input, then malloc room for n ints. Read n more numbers into the block, then print them doubled, one per line. Remember to free. With the input below, the output is:
6-24014
The checker feeds this input to your program.
#include <stdio.h>
#include <stdlib.h>
int main(void) {
int n;
scanf("%d", &n);
int *nums = NULL; // allocate here
return 0;
}
2Your own strdup
Write char *copy_string(char *s) that allocates exactly enough heap memory for a copy of s (do not forget the '\0'), copies it, and returns the new pointer. main changes the copy to show it is separate, then frees it. Expected:
original: brainlagcopy: Brainlag
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
char *copy_string(char *s) {
return s;
}
int main(void) {
char original[] = "brainlag";
char *copy = copy_string(original);
copy[0] = 'B';
printf("original: %s\n", original);
printf("copy: %s\n", copy);
free(copy);
return 0;
}