malloc和calloc的区别?

做有什么区别:

ptr = malloc (MAXELEMS * sizeof(char *));

或:

ptr = calloc (MAXELEMS, sizeof(char*));

什么时候使用calloc优于malloc是个好主意,反之亦然?

612679 次浏览

calloc()为您提供一个零初始化的缓冲区,而malloc()使内存未初始化。

对于较大的分配,主流操作系统下的大多数calloc实现将从操作系统(例如通过POSIXmmap(MAP_ANONYMOUS)或WindowsVirtualAlloc)获得已知为零的页面,因此它不需要在用户空间中写入它们。这就是malloc从操作系统获得更多页面的正常方式;calloc只是利用了操作系统的保证。

这意味着calloc内存仍然可以是“干净的”和懒惰分配的,并且写入时复制映射到系统范围内共享的零物理页。(假设系统具有虚拟内存。)例如,效果在Linux性能实验中可见。

一些编译器甚至可以为您优化malloc+memset(0)到calloc,但如果您想将内存归零,最好只在源代码中使用calloc。(或者如果您试图预先修复它以避免稍后出现页面错误,那么这种优化会挫败您的尝试。)

如果您在写入内存之前不打算读取内存,请使用malloc,以便它可以(潜在地)从其内部空闲列表中为您提供脏内存,而不是从操作系统获取新页面。(或者代替将空闲列表上的内存块归零以进行少量分配)。


如果没有操作系统,calloc的嵌入式实现可能会将calloc本身留给零内存,或者它不是一个花哨的多用户操作系统,它将页面归零以阻止进程之间的信息泄漏。

在嵌入式Linux上,malloc可以mmap(MAP_UNINITIALIZED|MAP_ANONYMOUS),它只对某些嵌入式内核启用,因为它在多用户系统上不安全。

一个鲜为人知的区别是,在具有乐观内存分配的操作系统中,例如Linux,malloc返回的指针在程序实际接触它之前不会被实际内存支持。

calloc确实触及内存(它在内存上写了零),因此您可以确保操作系统使用实际的RAM(或交换)支持分配。这也是它比malloc慢的原因(它不仅必须将其归零,操作系统还必须通过可能交换其他进程来找到合适的内存区域)

有关malloc行为的进一步讨论,请参阅实例这个so问题

分配的内存块大小没有区别。calloc只是用物理全零位模式填充内存块。在实践中,通常假设位于calloc分配的内存块中的对象具有初始值,就好像它们是用文字0初始化的一样,即整数应该具有0的值,浮点变量-值0.0,指针-适当的空指针值,等等。

然而,从迂腐的角度来看,calloc(以及memset(..., 0, ...))只能保证正确初始化(带零)类型unsigned char的对象。其他一切都不能保证被正确初始化,并且可能包含所谓的陷阱表示法,这导致未定义的行为。换句话说,对于除unsigned char之外的任何类型,上述全零位模式项可能代表非法值,陷阱表示。

后来,在C99标准的技术更正中,该行为被定义为所有整数类型(这是有道理的)。即,在当前的C语言中,你只能用calloc(和memset(..., 0, ...))初始化整数类型。从C语言的角度来看,用它来初始化其他任何东西在一般情况下都会导致未定义的行为。

在实践中,calloc是有效的,我们都知道:),但是你是否想使用它(考虑到上面的情况)取决于你。我个人更喜欢完全避免它,使用malloc代替并执行我自己的初始化。

最后,另一个重要的细节是calloc需要通过将元素大小乘以元素数量来计算最终的块大小内部。在这样做的同时,calloc必须注意可能的算术溢出。如果不能正确计算请求的块大小,它将导致分配不成功(空指针)。同时,你的malloc版本没有尝试观察溢出。它将分配一些“不可预测”的内存量,以防溢出发生。

calloc的一个经常被忽视的优势是(符合的实现)它将有助于保护您免受整数溢出漏洞的侵害。比较:

size_t count = get_int32(file);struct foo *bar = malloc(count * sizeof *bar);

vs.

size_t count = get_int32(file);struct foo *bar = calloc(count, sizeof *bar);

如果count大于SIZE_MAX/sizeof *bar,前者可能会导致微小的分配和随后的缓冲区溢出。在这种情况下,后者将自动失败,因为无法创建如此大的对象。

当然,您可能必须注意不符合要求的实现,这些实现只是忽略了溢出的可能性……如果这是您目标平台上的一个问题,无论如何您都必须对溢出进行手动测试。

有两个区别。
malloc()需要一个参数(所需内存以字节为单位),而calloc()需要两个参数。
其次,malloc()不初始化分配的内存,而calloc()将分配的内存初始化为ZERO。

  • calloc()分配一个内存区域,长度将是其参数的乘积。calloc用ZERO填充内存并返回指向第一个字节的指针。如果它找不到足够的空间,则返回NULL指针。

语法:ptr_var = calloc(no_of_blocks, size_of_each_block); ptr_var = calloc(n, s);

  • malloc()分配REQUSTED SIZE的单个内存块并返回指向第一个字节的指针。如果它无法定位请求的内存量,则返回空指针。

语法:ptr_var = malloc(Size_in_bytes);malloc()函数接受一个参数,即要分配的字节数,而calloc()函数接受两个参数,一个是元素数,另一个是要为每个元素分配的字节数。此外,calloc()将分配的空间初始化为零,而malloc()没有。

<stdlib.h>标头中声明的calloc()函数比malloc()函数有几个优势。

  1. 它将内存分配为给定大小的多个元素,并且
  2. 它初始化分配的内存,以便所有位都被分配零。

留档使calloc看起来像malloc,只是将内存初始化为零;这不是主要区别!calloc的想法是抽象写入时复制语义学以进行内存分配。当您使用calloc分配内存时,它都映射到初始化为零的相同物理页面。当分配内存的任何页面写入物理页面时,就会分配。这通常用于制作巨大的哈希表,例如,因为空的哈希部分没有任何额外的内存(页面)支持;它们愉快地指向单个零初始化页面,甚至可以在进程之间共享。

任何对虚拟地址的写入都会映射到一个页面,如果该页面是零页面,则分配另一个物理页面,将零页面复制到那里并将控制流返回给客户端进程。这与内存映射文件,虚拟内存等工作方式相同。

以下是关于该主题的一个优化故事:http://blogs.fau.de/hager/2007/05/08/benchmarking-fun-with-calloc-and-zero-pages/

Georg Hager的博客上的文章使用calloc()和零页面进行基准测试

使用calloc()分配内存时,请求的内存量不会立即分配。相反,属于内存块的所有页面都通过一些MMU魔法连接到包含所有零的单个页面(链接如下)。如果这些页面只被读取(在基准测试的原始版本中,数组b、c和d是如此),则数据从单个零页面提供,当然,该页面适合缓存。内存绑定循环内核就到这里。如果一个页面被写入(无论如何),发生故障,“真实”页面将被映射并零页面复制到内存。这被称为写时复制,这是一种众所周知的优化方法(我甚至在C++讲座中多次教授过)。之后,零读技巧不再适用于该页面,这就是为什么插入-据说是多余的-初始化循环后性能会低得多的原因。

calloc通常是malloc+memset到0

显式使用malloc+memset通常会稍微好一些,尤其是当你做以下事情时:

ptr=malloc(sizeof(Item));memset(ptr, 0, sizeof(Item));

这更好,因为编译器在编译时就知道sizeof(Item),编译器在大多数情况下会用尽可能好的指令替换它,以将内存归零。另一方面,如果memset发生在calloc中,分配的参数大小没有在calloc代码中编译,并且通常调用真正的memset,它通常包含逐字节填充直到长边界的代码,而不是循环在sizeof(long)块中填充内存,最后逐字节填充剩余空间。即使分配器足够聪明地调用一些aligned_memset,它仍然是一个通用循环。

一个值得注意的例外是,当你正在对一个非常大的内存块(大约power_of_two千字节)进行malloc/calloc时,在这种情况下,分配可以直接从内核完成。由于操作系统内核通常会将出于安全原因而放弃的所有内存清零,足够聪明的calloc可能会在不进行额外归零的情况下返回它。同样-如果你只是分配一些你知道很小的东西,那么在性能方面使用malloc+memset可能会更好。

区别1:

malloc()通常分配内存块并将其初始化为内存段。

calloc()分配内存块并将所有内存块初始化为0。

区别2:

如果你考虑malloc()语法,它只需要1个参数。考虑下面的例子:

data_type ptr = (cast_type *)malloc( sizeof(data_type)*no_of_blocks );

例如:如果您想为int类型分配10个内存块,

int *ptr = (int *) malloc(sizeof(int) * 10 );

如果您考虑calloc()语法,它将需要2个参数。考虑下面的示例:

data_type ptr = (cast_type *)calloc(no_of_blocks, (sizeof(data_type)));

例如:如果您想为int类型分配10个内存块并将其初始化为ZERO,

int *ptr = (int *) calloc(10, (sizeof(int)));

相似度:

malloc()calloc()如果不是类型转换,默认情况下都将返回空*。

还没有提到的差异:尺寸限制

void *malloc(size_t size)最多只能分配SIZE_MAX

void *calloc(size_t nmemb, size_t size);可以分配大约SIZE_MAX*SIZE_MAX

这种能力在许多具有线性寻址的平台中并不常用。此类系统将calloc()限制为nmemb * size <= SIZE_MAX

考虑一种名为disk_sector的512字节类型,代码想要使用很多个扇区。在这里,代码最多只能使用SIZE_MAX/sizeof disk_sector个扇区。

size_t count = SIZE_MAX/sizeof disk_sector;disk_sector *p = malloc(count * sizeof *p);

考虑以下内容,它允许更大的分配。

size_t count = something_in_the_range(SIZE_MAX/sizeof disk_sector + 1, SIZE_MAX)disk_sector *p = calloc(count, sizeof *p);

现在,如果这样的系统能够提供如此大的分配是另一回事。今天的大多数人都不会。然而,当SIZE_MAX为65535时,这种情况已经发生了很多年。鉴于摩尔定律,怀疑这种情况将在2030年左右发生,某些内存模型具有SIZE_MAX == 4294967295和100 GB的内存池。

malloc()calloc()是C标准库中允许动态存储分配的函数,这意味着它们都允许在运行时分配内存。

它们的原型如下:

void *malloc( size_t n);void *calloc( size_t n, size_t t)

两者主要有两个区别:

  • 行为:malloc()分配一个内存块,而不初始化它,从该块读取内容将导致垃圾值。另一方面,calloc()分配一个内存块并将其初始化为零,显然读取该块的内容将导致零。

  • 语法:malloc()取1个参数(要分配的大小),calloc()取两个参数(要分配的块数和每个块的大小)。

如果成功,两者的返回值都是指向分配的内存块的指针。否则,将返回NULL,指示内存分配失败。

示例:

int *arr;
// allocate memory for 10 integers with garbage valuesarr = (int *)malloc(10 * sizeof(int));
// allocate memory for 10 integers and sets all of them to 0arr = (int *)calloc(10, sizeof(int));

使用malloc()memset()可以实现与calloc()相同的功能:

// allocate memory for 10 integers with garbage valuesarr= (int *)malloc(10 * sizeof(int));// set all of them to 0memset(arr, 0, 10 * sizeof(int));

请注意,最好使用malloc()而不是calloc(),因为它更快。如果需要将值初始化为零,请改用calloc()

区块数:
malloc()分配单个请求内存块,
calloc()分配请求内存的多个块

初始化:
malloc()-不清除和初始化分配的内存。
calloc()-将分配的内存初始化为零。

速度:
malloc()很快。
calloc()比malloc()慢。

参数和语法:
malloc()需要1个参数:

  1. 字节

    • 要分配的字节数

calloc()需要2个参数:

  1. 长度

    • 要分配的内存块数
  2. 字节

    • 每个内存块要分配的字节数
void *malloc(size_t bytes);void *calloc(size_t length, size_t bytes);

内存分配方式:
malloc函数从可用堆中分配所需“大小”的内存。
calloc函数分配的内存大小等于'num*size'。

名字含义:
名称malloc表示“内存分配”。
名称calloc表示“连续分配”。

malloccalloc都分配内存,但calloc将所有位初始化为零,而malloc没有。

Calloc可以说相当于malloc+memset和0(其中memset将指定的内存位设置为零)。

因此,如果不需要初始化为零,那么使用malloc可能会更快。