Is calloc better than malloc?
Asked Answered
P

5

20

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.

Patron answered 21/12, 2016 at 13:4 Comment(18)
In C+ why you want calloc()?Demavend
C or C++? In C++ manual memory allocations, espcially the *alloc functions are frowned upon.Julietajulietta
The general rule is that if you have a problem P you use a solution X that solves P, and not Y that happens to solve P as a side effect. There's no substitute for critical thinking; programming by recipe book rarely works.Bertle
If it's for memory you don't need zero-initialized, why even chance paying extra for it?Ponytail
How should calloc break any program that works fine with malloc? Everyone who uses malloc should be aware that he cannot rely on the content of the buffer. If it happens to be all zeroes that is just one possible result of random content.Natalyanataniel
@NathanOliver: I'm a C++ programmer, thought I'd also add the C tag because the question is about C functions. I'm well aware of C++ memory management best practices, but there's one case when I feel malloc is the best way to allocate memory: when I need to allocate a buffer of a fixed size that's encapsulated in one of my classes.Patron
@VioletGiraffe - You'll have to run that by me to see the merit of malloc. I'll just use ::operator new if I really have to.Ponytail
@VioletGiraffe If you need a fixed sized buffer std::unique_ptr<T[]> is the way I would go. Now you have built in memory management.Julietajulietta
@NathanOliver: and runtime overhead. But yeah, I get the point.Patron
@VioletGiraffe What run time overhead? If you have a manually allocated pointer you still have to delete it at some point. std::unique_ptr just binds that deletion to the pointers scope so you do not have to remember about it. a std::unique_ptr with a default deleter should be just as efficient as doing it yourself with the benefit of not really being able to get it wrong.Julietajulietta
@NathanOliver: Is there no additional overhead in the unique pointer? No service structures, no thread-safety mechanisms? I have a hard time believing it's merely class unique_ptr {T* _ptr};Patron
@NathanOliver: In the interest of full disclosure, it's worth looking at the actual runtime cost that unique_ptr has over raw pointers: The facility for dynamic ownership transfer has a dynamic cost, namely something like p = make_unique<...>(...); needs to perform a null check on p first, even if you statically know already that it is null. Moreover, passing unique pointers across function calls in inferior on the Itanium ABI, because the object has to go on the stack, while a raw pointer is passed in a register. (That's an implementation mistake, not a design mistake, but still.)Bertle
@VioletGiraffe It pretty much is like you have it. There is no thread safety, no atomic reference counting, no null checks, It is meant to be as light weight as possible. It does store the deleter in the structure but thanks to empty base class optimization if it is just a empty deleter like a call to delete[] then that doesn't even make the size of the pointer bigger. In gcc sizeof(std::unique_ptr<int[]>) is 8 which is the same size as a single pointer(64 bit).Julietajulietta
@NathanOliver: Of course all this is very rarely significant compared to the enormous conceptual advantage that type-based ownership semantics afford us.Bertle
@KerrekSB Thanks for that. I forgot about that case.Julietajulietta
@NathanOliver: thanks, that's good to know.Patron
You don't care about performance until you know where your bottleneck is.Clareclarence
@n.m.: that approach often results in a program that's one large bottleneck as a whole, possibly with no clearly defined hot spots. Having said that - yes, early optimization is evil.Patron
D
13

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.

Demavend answered 21/12, 2016 at 13:9 Comment(5)
The zero-initialization aspect is quite clear. But what about lazy memory allocation that calloc() provides?Patron
@VioletGiraffe AFAIK, that is the property of the memory allocator, and applicable equally with malloc() and /or calloc() for a particular platform. Can you elaborate?Demavend
The links are in my question: https://mcmap.net/q/16202/-why-malloc-memset-is-slower-than-calloc and vorpus.org/blog/why-does-calloc-existPatron
If you need the first character in a char array to be '\0', just do it. calloc is overkill for such a simple initialization.Unilateral
@PeteBecker that was one example scenario, we can find more complex scenarios for sure where we need the whole memory to be initialized. :)Demavend
F
3

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.

Ferdelance answered 21/12, 2016 at 13:9 Comment(0)
G
2

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.

Greylag answered 22/12, 2016 at 7:58 Comment(0)
A
1

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?

Aerification answered 21/12, 2016 at 13:7 Comment(4)
I'm more interested in lazy allocation that calloc() provides.Patron
@VioletGiraffe Then you definitely want malloc. On Linux malloc returns a pointer not even pointing to real memory. The memory becomes real the first time the program accesses it.Aerification
@VioletGiraffe calloc() doesn't do lazy allocation on any common platform as of yet. malloc() however does.Daggna
@nos: I have links to two articles (well, an article and an SO answer) in my question that state otherwise. Are they false?Patron
O
1

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.

Oday answered 22/12, 2016 at 9:25 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.