Menu

calloc and realloc in C: Zeroed Memory and Growing an Array

calloc gives you zeroed memory and multiplies the size safely; realloc grows a block you already filled. This page covers both, the temporary-pointer idiom that keeps a failed realloc from leaking, and when calloc beats malloc plus memset.

This page includes runnable editors - edit, run, and see output instantly.

malloc answers one question: give me this many bytes. Two companions in <stdlib.h> answer the questions that come next - give me this many bytes, cleared (calloc), and I need the block I already have to be bigger (realloc).

calloc: Count, Size, and Zeros

void *calloc(size_t count, size_t size);

Two arguments instead of one, and the result is filled with zero bytes.

That is the classic case: a histogram or tally array where every slot must start at zero. With malloc you would need a loop; calloc does it as part of the allocation, often for free because the operating system already hands out fresh pages pre-zeroed.

The overflow argument

The split into two arguments is not cosmetic. Consider a count read from a file:

size_t n = huge_value_from_input;

int *a = malloc(n * sizeof *a);     /* the product can wrap around */
int *b = calloc(n, sizeof *b);      /* required to detect the overflow */

If n * sizeof *a exceeds what size_t can hold, it wraps to a small number, malloc succeeds with a tiny block, and every subsequent write runs far past the end. calloc is required by the standard to fail and return NULL instead. When a size comes from outside your program, that check is worth having.

What "zeroed" actually means

calloc writes zero bytes. For integers and characters that is the value zero, which is what you want. For pointers and floating-point values, all-bits-zero is a null pointer and 0.0 on every mainstream platform, but the C standard does not promise it. Code that must be strictly portable assigns those explicitly.

calloc vs malloc + memset

int *a = calloc(n, sizeof *a);

int *b = malloc(n * sizeof *b);
if (b != NULL) {
    memset(b, 0, n * sizeof *b);
}

They produce the same result. Prefer calloc: it is one line, it does the overflow check, and for large blocks it can avoid touching the memory at all. Prefer plain malloc when you are about to overwrite every byte anyway - zeroing a megabyte you are going to fill immediately is pure waste.

realloc: Changing the Size

void *realloc(void *p, size_t newsize);

realloc returns a block of newsize bytes whose contents match the old block up to the smaller of the two sizes. It may extend the block where it sits, or allocate a new one, copy, and free the old. You cannot tell which, so the only pointer you may use afterwards is the one it returned.

The tmp-pointer idiom

The three lines around tmp are the whole reason this function has a reputation. The tempting shorthand is broken:

/* BUG: leaks the original block when realloc fails */
p = realloc(p, newsize);
if (p == NULL) {
    return;
}

When realloc fails it returns NULL and leaves the original block allocated and unchanged. Assigning the result straight into p overwrites the only pointer to that block, so it can never be freed - a leak, and one that happens exactly when memory is already scarce. Assign to tmp, test tmp, then commit:

void *tmp = realloc(p, newsize);
if (tmp == NULL) {
    /* p is still valid; handle the failure however suits the caller */
    return 0;
}
p = tmp;

Two more behaviors worth knowing: realloc(NULL, n) behaves exactly like malloc(n), which lets a growth function handle its first call without a special case. And growing leaves the new bytes uninitialized - only the old contents are preserved.

Growing an Array: The Standard Pattern

Here is the pattern almost every C container uses - a length, a capacity, and doubling when they meet.

Two design points carry the weight here.

Doubling, not adding one. Growing by a fixed amount makes each push copy the whole array, so filling n elements costs roughly byte copies. Doubling makes the copies rare enough that each push costs constant time on average. The cap == 0 ? 4 : cap * 2 start handles the first push without a separate branch, because realloc(NULL, n) is just malloc.

The struct owns one allocation. v.data is freed exactly once, by whoever owns the struct. Writing that ownership rule down - in a comment, or by pairing every vec_init with a vec_free - is what keeps a growing container from leaking as it moves between functions.

Shrinking

realloc with a smaller size trims a block, which is useful after you have read an unknown amount of data into an over-sized buffer:

A failed shrink is not an error worth aborting for: the old, bigger block is still valid and still correct. This is the one case where ignoring the failure is the right call - but note it still goes through tmp, because the rule about not overwriting the live pointer does not change.

Which One to Reach For

You wantUse
Bytes, contents irrelevantmalloc(n * sizeof *p)
A count of items, cleared to zerocalloc(n, sizeof *p)
A count from untrusted inputcalloc, for the overflow check
The block you have, bigger or smallerrealloc through a tmp pointer
First allocation in a growth looprealloc(NULL, n) - no special case

All four are released by the same free, exactly once.

Common Mistakes

  • p = realloc(p, n) - leaks on failure. Always go through tmp.
  • Keeping an old pointer into the block. After realloc the data may have moved, so every saved interior pointer or index-derived address is stale. Indices survive a move; pointers do not.
  • Assuming the new bytes are zero. Only calloc zeroes. realloc leaves the growth region uninitialized.
  • calloc(n * size, 1) - that throws away the overflow check the two-argument form exists for.
  • Freeing the old pointer after a successful realloc. realloc already released it; a second free is a double free.

Frequently Asked Questions

What is the difference between calloc and malloc in C?

Two differences. calloc(n, size) takes the count and the element size separately and checks that multiplying them does not overflow, while malloc(n * size) can wrap silently. And calloc zero-fills the block, where malloc leaves it holding whatever was there before.

How does realloc work in C?

realloc(p, newsize) returns a block of the new size with your existing contents preserved up to the smaller of the two sizes. It may grow the block in place or move it, so the returned pointer is the only one you may keep using - the old one may already be freed.

Why should you assign realloc to a temporary pointer?

Because p = realloc(p, n) overwrites p with NULL when the call fails, and the original block is still allocated with nothing pointing at it - a leak. Assign to a tmp first, check it for NULL, and only then write p = tmp.

When should I use calloc instead of malloc?

When you genuinely need the memory zeroed (a counter array, a struct whose fields should start empty, a buffer you will fill partially) or when the size is a count times an element size that could be large. If you are going to overwrite every byte anyway, malloc skips the zeroing work.

Coddy programming languages illustration

Learn to code with Coddy

GET STARTED