malloc and free
Last lesson asked the heap for memory. This lesson covers malloc and free properly: how many bytes to ask for, what if you can't get them, what's in the memory you get, and when to give it back. Here's a full example first: five days of temperatures on the heap.
1#include <stdio.h>2#include <stdlib.h>3 4int main(void) {5 int *temps = malloc(5 * sizeof(int));6 if (temps == NULL) {7 printf("Out of memory\n");8 return 1;9 }10 for (int i = 0; i < 5; i++) {11 temps[i] = 20 + i;12 }13 printf("Day one %d degrees, last day %d degrees\n", temps[0], temps[4]);14 free(temps);15 temps = NULL;16 return 0;17}
Day one 20 degrees, last day 24 degrees
Using heap memory always takes these four steps:
| Step | How it's written here |
|---|---|
Ask: work out the bytes, call malloc | malloc(5 * sizeof(int)) |
Check: is it NULL? | if (temps == NULL) |
| Use: with indexes, like an array | temps[i] = 20 + i; |
Give back: free it when done | free(temps); |
Press Step: past line 5, a "20 bytes" block appears in the heap with an arrow from temps; each time around the loop, one more number lands in the block; past the free, the block turns gray, marked "freed". The last line sets temps to NULL and the arrow disappears too. The next lesson explains why you'd do that.
How many bytes
malloc only knows a number of bytes, not what you plan to put there. To hold n values of some type, write n * sizeof(type). Here sizeof(int) is 4 and sizeof(double) is 8:
1#include <stdio.h>2#include <stdlib.h>3 4int main(void) {5 int count = 3;6 double *prices = malloc(count * sizeof(double));7 prices[0] = 4.5;8 prices[1] = 12.0;9 prices[2] = 3.25;10 double total = 0;11 for (int i = 0; i < count; i++) {12 total += prices[i];13 }14 printf("%zu bytes, total %.2f\n", count * sizeof(double), total);15 free(prices);16 return 0;17}
24 bytes, total 19.75
(To keep them short, a few examples in this section and the next skip the NULL check. Your own programs shouldn't.)
Writing just the count and forgetting the sizeof is a very common mistake: malloc(3) asks for 3 bytes, not even enough for one int.
1#include <stdio.h>2#include <stdlib.h>3 4int main(void) {5 int *steps = malloc(3);6 steps[0] = 4200;7 steps[1] = 6100;8 printf("%d\n", steps[1]);9 free(steps);10 return 0;11}
heap-buffer-overflowWhat happened: Stopped at line 6. This goes past the end of an array (or a string). Real C would read or overwrite whatever is next in memory.
Real C won't warn you: the extra bytes land on top of something else, and the program may only act strangely later. Our engine stops right on line 6: the block has only 3 bytes, so even one 4-byte int goes past its end. Press "see how it went wrong" and look at the "3 bytes" block on the heap.
Tip: You can also write malloc(3 * sizeof *steps): *steps is an int, so sizeof *steps is 4. If you later change int to double, this line doesn't need to change.
Step past line 5. How many bytes does the heap block say?
1#include <stdio.h>2#include <stdlib.h>3 4int main(void) {5 double *weights = malloc(4 * sizeof(double));6 for (int i = 0; i < 4; i++) {7 weights[i] = 60.5 + i;8 }9 printf("%.1f\n", weights[3]);10 free(weights);11 return 0;12}
One double takes 8 bytes, and you asked for 4, so 4 × 8 = 32 bytes. All malloc gets is the number 32; it has no idea you plan to store 4 double values.
Checking for NULL
Memory isn't endless. When malloc can't get that much, it returns NULL. Check what you got before you use it:
1#include <stdio.h>2#include <stdlib.h>3 4int main(void) {5 char *big = malloc(100000000000);6 if (big == NULL) {7 printf("Can't get 100 GB\n");8 return 1;9 }10 big[0] = 'A';11 free(big);12 return 0;13}
Can't get 100 GB
100 GB at once is more than a normal computer can give, so malloc returned NULL, and the program printed one line and stopped. Without the check, big[0] = 'A'; would write through NULL and the program would crash. return 1; tells the system the program didn't finish cleanly.
malloc returns a void *, which means "an address, without saying what's there". In C it can go straight into an int *, a double *, a char * or any other pointer, with nothing extra to write.
What's in the memory you get
malloc only finds a spot; it doesn't care what's in it. Whatever was there before is still there, just like a variable you haven't assigned (lesson 2):
1#include <stdio.h>2#include <stdlib.h>3 4int main(void) {5 int *counts = malloc(3 * sizeof(int));6 counts[0] = 5;7 printf("%d %d\n", counts[0], counts[1]);8 free(counts);9 return 0;10}
use of uninitialized valueWhat happened: Stopped at line 7. A variable was used before it was given a value. In real C it would hold garbage, different on every run.
counts[1] was never given a value. Real C would print some unpredictable number, maybe different on every run; our engine stops on line 7 and tells you.
calloc: zeros from the start
For memory that's all zeros, use calloc. It takes two arguments: how many, and how many bytes each. It clears the whole block to 0:
1#include <stdio.h>2#include <stdlib.h>3 4int main(void) {5 int votes[8] = {2, 0, 1, 2, 2, 0, 1, 2};6 int *counts = calloc(3, sizeof(int));7 for (int i = 0; i < 8; i++) {8 counts[votes[i]]++;9 }10 for (int i = 0; i < 3; i++) {11 printf("Option %d: %d votes\n", i, counts[i]);12 }13 free(counts);14 return 0;15}
Option 0: 2 votes Option 1: 2 votes Option 2: 4 votes
Counting starts from 0, so calloc is just right. Press Step: past line 6, the heap block is "12 bytes" with all three cells at 0; after that, each vote adds 1 to one cell.
free it when you're done
free(p) gives back the block p points at, and that memory may later be handed to someone else. A few rules:
- Every block from
mallocorcallocmust be freed in the end, once and only once. - What you
freemust be the addressmallocorcallocgave you, not the address of an ordinary variable. - After
free, don't touch what was in the block. free(NULL)does nothing, so it's safe.
1#include <stdio.h>2#include <stdlib.h>3 4int main(void) {5 int *a = malloc(2 * sizeof(int));6 int *b = malloc(4 * sizeof(int));7 a[0] = 1;8 b[0] = 2;9 printf("%d %d\n", a[0], b[0]);10 free(a);11 free(b);12 return 0;13}
1 2
Press Step: two blocks appear on the heap, one "8 bytes · line 5", one "16 bytes · line 6". Past line 10 the first one turns gray; past line 11, the second does too. When the program ends, no block is left un-freed. What happens when you break these rules is the next lesson, one rule at a time.
Practice
Write the code, then press Check. A hint costs nothing; the answer is there when you need it.
Average score
✓Get 4 int values on the heap, check for NULL, put in 78, 85, 90 and 77, print Average: 82.50, then free it.
Average: 82.50
1#include <stdio.h>2#include <stdlib.h>3 4int main(void) {5 6 return 0;7}
Counting dice
✓rolls holds 10 dice rolls (1 to 6). Use calloc to get 7 int counters (indexes 1 to 6 do the counting), count how many times each number came up, print it as 1: 2, one per line, then free it.
1: 2 2: 1 3: 2 4: 1 5: 1 6: 3
1#include <stdio.h>2#include <stdlib.h>3 4int main(void) {5 int rolls[10] = {3, 6, 1, 3, 5, 6, 6, 2, 1, 4};6 7 return 0;8}
Fix it
✓This program gets some memory, but goes out of bounds as soon as it uses it. Fix the malloc line so it prints 9.5 7.0 8.5.
9.5 7.0 8.5
1#include <stdio.h>2#include <stdlib.h>3 4int main(void) {5 double *ratings = malloc(3);6 if (ratings == NULL) {7 return 1;8 }9 ratings[0] = 9.5;10 ratings[1] = 7.0;11 ratings[2] = 8.5;12 printf("%.1f %.1f %.1f\n", ratings[0], ratings[1], ratings[2]);13 free(ratings);14 return 0;15}
What you learned
malloc(n * sizeof(type))gets room fornvalues on the heap and returns the block's address;malloconly knows bytes.- When it can't get the memory,
mallocreturnsNULL: check before you use it. mallocmemory isn't cleared, so assign before you read; for all zeros, usecalloc(count, bytes_each).freeit when you're done: once per block, only addresses frommalloc/calloc, andfree(NULL)does nothing.- When you step, each heap block shows its size and the line that asked for it; after
freeit turns gray, marked "freed".