有没有理由不使用calloc而不是malloc?

时间:2022-05-18 11:50:48

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?

我前几天刚刚学习了C calloc()函数。阅读了它的描述以及它与malloc(1,2)的不同之处,我认为,作为一个非嵌入式程序员,我应该总是使用calloc()。但情况确实如此吗?

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.

我有一个保留是访问calloc() - ed内存的额外延迟,但我也想知道是否有从malloc()切换到calloc()的情况会以更严重的方式破坏程序。

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.

P. S. calloc()的零初始化方面对我来说非常清楚。我有兴趣了解的是calloc()和malloc()之间的另一个区别 - calloc()提供的惰性内存分配。如果您要专注于内存初始化方面,请不要发布答案。

5 个解决方案

#1


7  

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).

    如果您是第一次写入已分配的内存,则malloc()更好(开销更少)。

    Example: Consider the following scenario

    示例:请考虑以下方案

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

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

    在这里,使用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.

    如果有可能使用分配的内存进行read-before-write,则在初始化内存时使用calloc()。这样就可以避免单元化内存先读后读取方案的问题,该方案会调用未定义的行为。

    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.

    这里,strcat()需要第一个参数是一个字符串,并且使用malloc()来分配不能保证。当calloc()对内存进行零初始化时,它将在此处起作用,因此,calloc()是这种情况的方法。

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

详细说明第二种情况,引用C11,第7.24.3.1章(按照我的意见)

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. [....]

strcat()函数将s2指向的字符串副本(包括终止空字符)附加到s1指向的字符串的末尾。 s2的初始字符会覆盖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

因此,在这种情况下,目标指针应该是指向字符串的指针。通过calloc()进行分配可以保证,尽管如我们所知,使用malloc()进行分配不能保证章节§7.22.3.4

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

malloc函数为一个对象分配空间,该对象的大小由size指定,其值是不确定的。


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.

通过calloc()建议malloc()的一种可能方案是编写用于单元/集成测试的测试存根。在这种情况下,使用calloc()可以隐藏潜在的错误,这些错误是通过类似于后者的情况到达的。

#2


2  

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

malloc和calloc之间的主要区别在于calloc将零初始化缓冲区,而malloc将使内存保持未初始化状态。

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.

作为标记C ++的旁注:使用new / delete的手动内存使用在现代C ++中是不受欢迎的(除了极少数的内存池等)。使用malloc / free更为罕见,应该非常谨慎地使用。

#3


1  

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

使用calloc进行零填充分配,但仅在真正需要填零时使用。

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

你应该总是使用calloc(count,size)而不是buff = malloc(total_size); memset的(浅黄色,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.

对零记忆的调用是关键。 malloc和calloc都被转换为OS调用,这些调用进行了大量优化,尽可能使用硬件技巧等等。但是操作系统几乎无法使用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.

另一方面,何时需要对已分配的内存进行零填充?唯一常见的用途是零端任意长度元素,例如C字符串。如果是这样的话,请务必使用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.

但是如果你分配元素是固定长度的结构,或者用它们携带任意大小元素的长度(例如C ++字符串和向量),那么零填充根本就没有用,如果你试图依赖它在它上面,它可能导致棘手的错误。

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.

假设您编写自定义链接列表,并决定通过为calloc分配节点的内存来跳过指向下一个节点的指针的清零。它工作正常,然后有人使用自定义位置new,它不是零填充。麻烦的是,有时它将是零填充,并且可以通过所有常规测试,进入生产,并且它会崩溃,有时崩溃,可怕的不可重复的错误。

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:

出于调试目的,零填充通常也不是那么好。 0太常见了,你很少能写出像assert(size)这样的东西;因为它通常也是一个有效的值,你用if(!size)处理它,而不是用asserts处理它。在调试器上它也不会引起你的注意,你的记忆中通常都会有零。最佳做法是避免长度的无符号类型(有符号长度对于运行时错误处理和一些最常见的溢出检查也很有用)。所以,而buff = malloc(total_size); memset(buff,0,total_size)是要避免的,以下是可以的:

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.

在调试模式下,运行时库甚至操作系统有时会为您执行此操作,例如,请查看有关VC ++特定的标记值的这篇优秀文章。

#4


0  

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?

这都是关于你想要记忆的事情。 malloc返回未初始化(可能甚至不是真实的)内存。 calloc返回真实,零内存。如果你需要它零,那么是的,calloc是你最好的选择。如果你不这样做,为什么在不需要延迟命中时支付零费用呢?

#5


0  

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

malloc()在C代码中比calloc()更常见。

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

对“malloc”的文本搜索将错过calloc()调用。

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

替换图书馆通常会有mymalloc(),myrealloc(),myfree()但不包含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.

指针和实数的零初始化实际上并不能保证具有预期的效果,尽管在每个主要平台上,对于指针,所有位零都为NULL,对于实数,所有位为0.0。

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.

calloc()倾向于隐藏错误。调试malloc通常设置一个类似DEADBEEF的填充模式,它会计算为一个较大的负数,看起来不像真实的数据。因此程序很快崩溃,并且使用调试器,错误被刷新。

#1


7  

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).

    如果您是第一次写入已分配的内存,则malloc()更好(开销更少)。

    Example: Consider the following scenario

    示例:请考虑以下方案

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

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

    在这里,使用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.

    如果有可能使用分配的内存进行read-before-write,则在初始化内存时使用calloc()。这样就可以避免单元化内存先读后读取方案的问题,该方案会调用未定义的行为。

    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.

    这里,strcat()需要第一个参数是一个字符串,并且使用malloc()来分配不能保证。当calloc()对内存进行零初始化时,它将在此处起作用,因此,calloc()是这种情况的方法。

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

详细说明第二种情况,引用C11,第7.24.3.1章(按照我的意见)

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. [....]

strcat()函数将s2指向的字符串副本(包括终止空字符)附加到s1指向的字符串的末尾。 s2的初始字符会覆盖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

因此,在这种情况下,目标指针应该是指向字符串的指针。通过calloc()进行分配可以保证,尽管如我们所知,使用malloc()进行分配不能保证章节§7.22.3.4

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

malloc函数为一个对象分配空间,该对象的大小由size指定,其值是不确定的。


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.

通过calloc()建议malloc()的一种可能方案是编写用于单元/集成测试的测试存根。在这种情况下,使用calloc()可以隐藏潜在的错误,这些错误是通过类似于后者的情况到达的。

#2


2  

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

malloc和calloc之间的主要区别在于calloc将零初始化缓冲区,而malloc将使内存保持未初始化状态。

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.

作为标记C ++的旁注:使用new / delete的手动内存使用在现代C ++中是不受欢迎的(除了极少数的内存池等)。使用malloc / free更为罕见,应该非常谨慎地使用。

#3


1  

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

使用calloc进行零填充分配,但仅在真正需要填零时使用。

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

你应该总是使用calloc(count,size)而不是buff = malloc(total_size); memset的(浅黄色,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.

对零记忆的调用是关键。 malloc和calloc都被转换为OS调用,这些调用进行了大量优化,尽可能使用硬件技巧等等。但是操作系统几乎无法使用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.

另一方面,何时需要对已分配的内存进行零填充?唯一常见的用途是零端任意长度元素,例如C字符串。如果是这样的话,请务必使用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.

但是如果你分配元素是固定长度的结构,或者用它们携带任意大小元素的长度(例如C ++字符串和向量),那么零填充根本就没有用,如果你试图依赖它在它上面,它可能导致棘手的错误。

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.

假设您编写自定义链接列表,并决定通过为calloc分配节点的内存来跳过指向下一个节点的指针的清零。它工作正常,然后有人使用自定义位置new,它不是零填充。麻烦的是,有时它将是零填充,并且可以通过所有常规测试,进入生产,并且它会崩溃,有时崩溃,可怕的不可重复的错误。

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:

出于调试目的,零填充通常也不是那么好。 0太常见了,你很少能写出像assert(size)这样的东西;因为它通常也是一个有效的值,你用if(!size)处理它,而不是用asserts处理它。在调试器上它也不会引起你的注意,你的记忆中通常都会有零。最佳做法是避免长度的无符号类型(有符号长度对于运行时错误处理和一些最常见的溢出检查也很有用)。所以,而buff = malloc(total_size); memset(buff,0,total_size)是要避免的,以下是可以的:

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.

在调试模式下,运行时库甚至操作系统有时会为您执行此操作,例如,请查看有关VC ++特定的标记值的这篇优秀文章。

#4


0  

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?

这都是关于你想要记忆的事情。 malloc返回未初始化(可能甚至不是真实的)内存。 calloc返回真实,零内存。如果你需要它零,那么是的,calloc是你最好的选择。如果你不这样做,为什么在不需要延迟命中时支付零费用呢?

#5


0  

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

malloc()在C代码中比calloc()更常见。

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

对“malloc”的文本搜索将错过calloc()调用。

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

替换图书馆通常会有mymalloc(),myrealloc(),myfree()但不包含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.

指针和实数的零初始化实际上并不能保证具有预期的效果,尽管在每个主要平台上,对于指针,所有位零都为NULL,对于实数,所有位为0.0。

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.

calloc()倾向于隐藏错误。调试malloc通常设置一个类似DEADBEEF的填充模式,它会计算为一个较大的负数,看起来不像真实的数据。因此程序很快崩溃,并且使用调试器,错误被刷新。