A structured lesson workspace with readable content, hands-on examples, and a clean path to completion.
malloc gives you uninitialized memory. calloc gives you zero-initialized memory. That is the functional difference. The secondary difference is in the API: malloc takes one size argument, calloc takes a count and an element size and multiplies them internally. That internal multiplication includes an overflow check that your manual n * sizeof(int) does not. This makes calloc marginally safer against integer overflow in the size calculation.
#include <stdio.h>#include <stdlib.h>int main(void) {int n = 5;int *m = malloc(n * sizeof(int));int *c = calloc(n, sizeof(int));printf("malloc contents (uninitialized):\n");for (int i = 0; i < n; i++) {printf(" m[%d] = %d\n", i, m[i]);}printf("calloc contents (zeroed):\n");for (int i = 0; i < n; i++) {printf(" c[%d] = %d\n", i, c[i]);}free(m);free(c);return 0;}
On this run, malloc happened to return zeroed memory because the OS gave us fresh pages. That is not guaranteed. Run the program in a loop and you will eventually see garbage values from a previously freed block. calloc guarantees zeros. malloc guarantees nothing. If your logic depends on initial values being zero, use calloc or memset after malloc.
malloc(n * sizeof(int)) computes the multiplication in your code. If n is large enough that n * sizeof(int) overflows a size_t, you get a small allocation and then write past its bounds. calloc(n, sizeof(int)) performs the multiplication internally and returns NULL if it would overflow. This is a real security consideration when n comes from user input or a file header.
#include <stdio.h>#include <stdlib.h>#include <stdint.h>int main(void) {size_t huge = SIZE_MAX / 2;void *m = malloc(huge * sizeof(int));printf("malloc(%zu * %zu): %s\n", huge, sizeof(int),m ? "allocated (bad!)" : "NULL");free(m);void *c = calloc(huge, sizeof(int));printf("calloc(%zu, %zu): %s\n", huge, sizeof(int),c ? "allocated" : "NULL (overflow caught)");free(c);return 0;}
The malloc version wrapped around and allocated a tiny block. Writing n elements into it would overflow the heap. calloc detected the overflow and returned NULL. In security-sensitive code where sizes come from untrusted input, calloc's built-in check is a meaningful defense.
Use calloc when you need zeroed memory or when the size calculation involves untrusted input. Use malloc when you will immediately overwrite the entire block and the zeroing overhead is wasted. In practice, the performance difference is negligible for most allocations. Correctness should drive the choice, not micro-optimization. Next: realloc, for resizing allocations in place.
Finish the lesson once you have worked through the material. This awards ★ 30 XP.