Is calloc better than malloc?

7.9k views Asked by At

I have just learned about the C calloc() function the other day. Having read its description and how it differs from malloc (1, 2), I get the idea that, as a non-embedded programmer, I should always use calloc(). But is that really the case?

One reservation I have is the extra delay for accessing the calloc()-ed memory, but I also wonder if there are cases when switching from malloc() to calloc() will break the program in some more serious way.

P. S. The zero-initializing aspect of calloc() is quite clear to me. What I'm interested in learning about is the other difference between calloc() and malloc() - lazy memory allocation provided by calloc(). Please don't post an answer if you're going to focus purely on the memory initialization aspect.

5

There are 5 answers

0
Cory Kramer On

The main difference between malloc and calloc is that calloc will zero-initialize your buffer, and malloc will leave the memory uninitialized.

This gets to the common programming idiom of "don't pay for what you don't use". In other words, why zero-initialize something (which has a cost) if you don't necessarily need to (yet)?

As a side note since you tagged C++: manual memory usage using new/delete is frowned upon in modern C++ (except in rare cases of memory pools, etc). Use of malloc/free is even more rare and should be used very sparingly.

5
Sourav Ghosh On

This is really a situation-dependent decision. Rule of thumb is

  • If you're first writing into the allocated memory, malloc() is better (less possible overhead).

    Example: Consider the following scenario

    char * pointer = NULL;
    
    //allocation
    
    strcpy(pointer, source);
    

    here, allocation can be very well using malloc().

  • If there's a possibility of read-before-write with the allocated memory, go for calloc(), as it initializes memory. This way you can avoid the problem with unitialized memory read-before-write scenario which invokes undefined behavior.

    Example:

    char * pointer = NULL;
    
    //allocation
    
    strcat(pointer, source);
    

    Here, strcat() needs the first argument to be a string already, and using malloc() to allocate cannot guarantee that. As calloc() zero-initializes the memory, it will serve the purpose here and thus, calloc() is the way to go for this case.

To elaborate the second scenario, quoting from C11, chapter §7.24.3.1 (follow my emphasis)

The strcat() function appends a copy of the string pointed to by s2 (including the terminating null character) to the end of the string pointed to by s1. The initial character of s2 overwrites the null character at the end of s1. [....]

So, in this case, the destination pointer should be a pointer to a string. Allocating via calloc() guarantees that while allocating using malloc() cannot guarantee that, as we know, from chapter §7.22.3.4

The malloc function allocates space for an object whose size is specified by size and whose value is indeterminate.


EDIT:

One possible scenario where malloc() is advised over calloc(), is writing test stubs used for unit / integration testing. In that case, use of calloc() can hide potential bugs which arrive with cases similar to the later one.

0
Malcolm McLean On

malloc() is far more common in C code than calloc().

A text search for "malloc" will miss the calloc() calls.

Replacement libraires will often have mymalloc(), myrealloc(), myfree() but not mycalloc().

Zero-initialisation of pointers and reals isn't actually guaranteed to have the expected effect, though on every major platform all bits zero is NULL for a pointer and 0.0 for a real.

calloc() tends to hide bugs. Debug malloc usually sets a fill pattern like DEADBEEF which evaluates to a large negative number and doesn't look like real data. So the program quickly crashes and, with a debugger, the error is flushed out.

0
Mike Tyukanov On

Use calloc for zero-filled allocations, but only when the zero-filling is really needed.

You should always use calloc(count,size) instead of buff=malloc(total_size); memset(buff,0,total_size).

The call to zero-memset is the key. Both malloc and calloc are translated into OS calls which do lots of optimizations, use hardware tricks whenever possible, etc. But there is little that OS can do with memset.

On the other hand, when do you need to zero-fill the allocated memory? The only common use is for zero-ended arbitrary length elements, such as C-strings. If that's the case, sure, go with calloc.

But if you allocate the structures in which the elements are either fixed-length or carry the length of arbitrary-sized elements with them (such as C++ strings and vectors), zero-filling is not helpful at all, and if you try to rely on it, it can lead to tricky bugs.

Suppose you write your custom linked list, and decide to skip the zeroing of the pointer to the next node by allocating the memory for the node with calloc. It works fine, then someone uses it with custom placement new, which doesn't zero-fill. Trouble is, sometimes it will be zero-filled, and can pass all the usual testing, go in production, and there it will crash, crash sometimes, the dreaded unrepeatable bug.

For debug purposes, zero-filling is usually not that good, either. 0 is too common, you rarely can write something like assert(size); because it's usually a valid value, too, you handle it with if(!size), not with asserts. On the debugger it won't catch your eye, either, there are usually zeros everywhere in your memory. The best practice is to avoid unsigned types for the lengths (signed lengths can be useful for runtime error handling and some of the most common overflow checks, too). So, while buff=malloc(total_size); memset(buff,0,total_size) is to be avoided, the following is OK:

const signed char UNINIT_MEM=MY_SENTINEL_VALUE;
buff=malloc(total_size);
#if DEBUG_MEMORY
memset(buff,UNINIT_MEM,total_size);
#endif

In debug mode, runtime library or even OS do this for you sometimes, for example check this excellent post on VC++-specific sentinel values.

4
Paul Evans On

It's all about what you want to do with the memory. malloc returns uninitialized (and possibly not even real yet) memory. calloc return real, zero'ed memory. If you need it zero'ed, then yes, calloc is your best option. If you don't, why pay for zero'ing with a latency hit when you don't need it?