Dynamic Memory Allocation in C: Calloc vs Malloc

Dynamic memory allocation in C involves allocating storage, initializing it, resizing it when needed, and releasing it. This guide covers malloc, calloc, realloc, and free. The practical choice in calloc vs malloc is how you want the data initialized. Use malloc when you will initialize it before reading it. Use calloc when you need zeroed storage, such as integer counters that should start at zero.
Both malloc and calloc return a null pointer on failure. Include <stdlib.h>, check the result, and release successful allocations with free when you are done.
The difference at a glance
| Property | malloc |
calloc |
|---|---|---|
| Arguments | Total byte count | Element count and element size |
| Example | malloc(count * sizeof *p) |
calloc(count, sizeof *p) |
| Initial contents | Uninitialized | All bits zero |
| Read integer elements immediately? | No: initialize them first | Yes, after a successful allocation |
| Release storage | free(p) |
free(p) |
Zero bits represent integer zero. Don't assume that is true for other types in C. Zeroed bytes are not guaranteed to be null pointers or to be floating-point zero. The allocation functions can be found in the C committee draft, section 7.22.3 (link).
A working calloc example
#include <stdio.h>
#include <stdlib.h>
int main(void)
{
size_t count = 4;
int *scores = calloc(count, sizeof *scores);
if (scores == NULL) {
fputs("Allocation failed.\n", stderr);
return 1;
}
scores[2] = 9;
for (size_t i = 0; i < count; ++i) {
printf("%d%s", scores[i], i + 1 == count ? "\n" : " ");
}
free(scores);
return 0;
}
Successful output:
0 0 9 0
The integer elements will start at zero, and no initialization loop is needed in this case.
A working malloc example
This example program explicitly sets the value of every element before reading it. It also checks the size multiplication, a pattern that is generally useful and safe to use, especially in the case when the value of the count is input.
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
int main(void)
{
size_t count = 4;
int *scores = NULL;
if (count > SIZE_MAX / sizeof *scores) {
fputs("Requested array is too large.\n", stderr);
return 1;
}
scores = malloc(count * sizeof *scores);
if (scores == NULL) {
fputs("Allocation failed.\n", stderr);
return 1;
}
for (size_t i = 0; i < count; ++i) {
scores[i] = (int)i * 10;
printf("%d%s", scores[i], i + 1 == count ? "\n" : " ");
}
free(scores);
return 0;
}
Successful output:
0 10 20 30
Don't try to print garbage values by reading uninitialized data as it is neither safe nor useful.
Grow an allocation with realloc
Use realloc to resize an allocation. Store the result in a temporary pointer until you know whether the request succeeded, so a failed request does not overwrite your only pointer to the original storage.
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
int main(void)
{
int *values = malloc(2 * sizeof *values);
if (values == NULL) {
return 1;
}
values[0] = 10;
values[1] = 20;
size_t new_count = 4;
if (new_count > SIZE_MAX / sizeof *values) {
free(values);
return 1;
}
int *grown = realloc(values, new_count * sizeof *values);
if (grown == NULL) {
free(values);
return 1;
}
values = grown;
values[2] = 30;
values[3] = 40;
for (size_t i = 0; i < new_count; ++i) {
printf("%d%s", values[i], i + 1 == new_count ? "\n" : " ");
}
free(values);
return 0;
}
On success, the example prints 10 20 30 40. The existing elements are preserved, and the new elements are initialized explicitly. Growing an allocation does not zero the new portion, even if the original block came from calloc.
This example requests a nonzero size. Do not use realloc(p, 0) as a portable substitute for free(p); zero-size behavior depends on the C version and implementation.
Which is faster?
Neither malloc nor calloc is always faster. Allocation size, allocator behavior, operating-system behavior, and subsequent writes all affect the cost. Choose the initialization semantics you need, then benchmark your workload if allocation performance matters.
Do I need to cast the returned pointer in C?
No. C permits conversion from void * to an object pointer. Include the proper header and use sizeof *pointer to tie the allocation size to the pointed-to type. C++ has different rules; see GCC vs G++.
Can I use a pointer after free?
No. After releasing an allocation, stop using pointers into it. Decide which function owns each allocation and when it may be freed. Other pointers into the allocation become invalid when its lifetime ends.
Run the examples in the online C compiler. Change the element values while keeping the initialization, bounds checks, and cleanup intact.