关于C#:malloc和calloc之间的区别?

Difference between malloc and calloc?

做与做有什么区别:

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

或:

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

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


calloc()0初始化缓冲区,而malloc()使内存未初始化。

编辑:

清空内存可能需要一些时间,因此如果性能有问题,您可能需要使用malloc()。如果初始化内存更重要,请使用calloc()。例如,calloc()可以为您节省一个对memset()的呼叫。


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

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

如需进一步讨论malloc的行为,请参见本问题。


calloc的一个经常被忽视的优点是(符合的实现)它将帮助您抵御整数溢出漏洞。比较:

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

VS

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

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

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


文档使calloc看起来像malloc,它只对内存进行零初始化;这不是主要区别!calloc的思想是为内存分配放弃copy-on-write语义。当使用calloc分配内存时,所有内存都映射到初始化为零的同一物理页。当分配内存的任何页写入物理页时,分配内存。这通常用于生成巨大的哈希表,例如,因为哈希的空部分没有任何额外的内存(页)支持;它们很高兴地指向单个零初始化页,甚至可以在进程之间共享该页。

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

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


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

不过,从迂腐的角度来看,callocmemset(..., 0, ...)只保证正确初始化(用零)unsigned char类型的对象。其他所有内容都不能保证正确初始化,并且可能包含所谓的陷阱表示,这会导致未定义的行为。换句话说,对于除unsigned char以外的任何类型,上述所有零位模式都可能表示非法值,即陷阱表示。

后来,在C99标准的技术勘误中,对所有整数类型(这是有意义的)定义了行为。也就是说,在当前的C语言中,只能用callocmemset(..., 0, ...)初始化整数类型。从C语言的角度来看,在一般情况下,使用它初始化任何其他东西都会导致未定义的行为。

实际上,我们都知道,calloc是有效的,但是您是否愿意使用它(考虑到上面的内容)取决于您。我个人更喜欢完全避免它,而是使用malloc,并执行我自己的初始化。

最后,另一个重要的细节是,需要calloc通过将元素大小乘以元素数量来计算内部的最终块大小。同时,calloc必须注意可能的算术溢出。如果无法正确计算请求的块大小,将导致分配失败(空指针)。同时,您的malloc版本不尝试监视溢出。它将分配一些"不可预测"的内存量,以防发生溢出。


从一篇关于calloc()和georg hager博客上的零页面的有趣的基准测试文章中

When allocating memory using calloc(), the amount of memory requested is not allocated right away. Instead, all pages that belong to the memory block are connected to a single page containing all zeroes by some MMU magic (links below). If such pages are only read (which was true for arrays b, c and d in the original version of the benchmark), the data is provided from the single zero page, which – of course – fits into cache. So much for memory-bound loop kernels. If a page gets written to (no matter how), a fault occurs, the"real" page is mapped and the zero page is copied to memory. This is called copy-on-write, a well-known optimization approach (that I even have taught multiple times in my C++ lectures). After that, the zero-read trick does not work any more for that page and this is why performance was so much lower after inserting the – supposedly redundant – init loop.


calloc一般为malloc+memset至0。

一般来说,明确使用malloc+memset稍微好一点,尤其是当您在执行以下操作时:

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

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

一个值得注意的例外是,当您对一个非常大的内存块执行malloc/calloc操作时(一些功率为2千字节),在这种情况下,可以直接从内核进行分配。由于操作系统内核通常会将它们出于安全原因而释放的所有内存归零,因此,足够智能的calloc可能只会在oud额外归零的情况下返回内存。再次说明——如果你只是分配一些你知道很小的东西,那么你最好在性能方面使用malloc+memset。


差异1:malloc()通常分配内存块,并初始化内存段。calloc()分配内存块并将所有内存块初始化为0。

差异2:如果考虑malloc()语法,它只需要1个参数。请考虑下面的示例:

data_type ptr=(cast_type*)malloc(sizeof(data_type)*no_of_块);

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

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

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

data_type ptr=(cast_type*)calloc(没有_块的_,(sizeof(data_type));

例如:如果要为int类型分配10个内存块,并将所有内存块初始化为零,

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

相似性:

如果不是类型化的话,malloc()和calloc()都将默认返回void*!


有两个区别。首先,是参数个数。malloc()接受一个参数(字节所需的内存),而calloc()需要两个参数。其次,malloc()不初始化分配的内存,而calloc()将分配的内存初始化为零。

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

语法:ptr_var=(cast_type *)calloc(no_of_blocks , size_of_each_block);。即ptr_var=(type *)calloc(n,s);

  • malloc()分配一个所需大小的内存块,并返回指向第一个字节的指针。如果找不到所需的内存量,则返回一个空指针。

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


头中声明的calloc()函数与malloc()函数相比,提供了一些优势。

  • 它以给定大小的若干元素的形式分配内存,以及
  • 它初始化分配的内存,以便所有位零。

  • 一个尚未提到的区别:大小限制

    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个扇区。

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

    考虑下面允许更大的分配。

    1
    2
    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千兆字节中。


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

    其原型如下:

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

    两者之间主要有两个区别:

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

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

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

    例子:

    1
    2
    3
    4
    5
    6
    7
    int *arr;

    // allocate memory for 10 integers with garbage values
    arr = (int *)malloc(10 * sizeof(int));

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

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

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

    注意,由于malloc()calloc()更快,因此最好使用它。如果需要零初始化值,请使用calloc()