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

时间:2016-12-21 13:04:21

标签: c++ c malloc dynamic-allocation calloc

前几天我刚刚了解了C calloc()函数。阅读了它的描述以及它与malloc12)之间的区别,我认为,作为非嵌入式程序员,我应该始终使用calloc()。但情况确实如此吗?

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

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

5 个答案:

答案 0 :(得分:7)

这实际上是一种依赖情况的决定。经验法则是

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

    示例:请考虑以下方案

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

    在这里,使用malloc()进行分配可以很好。

  • 如果有可能使用分配的内存进行先读后读,请转到calloc(),因为它会初始化内存。这样就可以避免调用undefined behavior的单元化内存先读后方案的问题。

    示例:

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

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

详细阐述第二种情况,引用C11,章节§7.24.3.1(跟随我的重点

  

strcat()函数附加s2指向的字符串的副本(包括   终止空字符)到s1 指向的字符串的结尾。最初的角色   s2的覆盖范围覆盖s1末尾的空字符。 [....]

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

  

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

编辑:

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

答案 1 :(得分:2)

Maven-->usersettings-->C:\Users\Jayanthraman\.m2\repository\settings.xml malloc之间的主要区别在于calloc会对您的缓冲区进行零初始化,calloc会使内存保持未初始化状态。

这可以达到常见的编程习惯用语" 不为你不使用"付出代价。换句话说,如果你不一定需要(还有),为什么零初始化某些东西(有成本)?

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

答案 2 :(得分:1)

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

您应始终使用calloc(count,size)代替buff=malloc(total_size); memset(buff,0,total_size)

调用零 - memset是关键。 malloccalloc都被转换为OS调用,这些调用会进行大量优化,尽可能使用硬件技巧等等。但操作系统几乎无法对memset执行操作。

另一方面,何时需要对已分配的内存进行零填充?唯一常见的用途是零端任意长度元素,例如C字符串。如果是这种情况,请务必使用calloc

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

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

出于调试目的,零填充通常也不是那么好。 0很常见,你很少能写assert(size);之类的东西,因为它通常也是一个有效的值,你用if(!size)来处理它,而不是用断言来处理它。在调试器上它也不会引起你的注意,你的记忆中通常都有零。最佳做法是避免长度的无符号类型(有符号长度对于运行时错误处理和一些最常见的溢出检查也很有用)。因此,虽然要避免使用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

在调试模式下,运行时库甚至操作系统有时会为您执行此操作,例如检查this excellent post on VC++-specific sentinel values

答案 3 :(得分:0)

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

答案 4 :(得分:0)

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

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

替换图书馆通常会有mymalloc(),myrealloc(),myfree()但不包含mycalloc()。

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

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