malloc和calloc之间的区别?

use*_*033 743 c malloc calloc

做的有什么区别:

ptr = (char **) malloc (MAXELEMS * sizeof(char *));
Run Code Online (Sandbox Code Playgroud)

要么:

ptr = (char **) calloc (MAXELEMS, sizeof(char*));
Run Code Online (Sandbox Code Playgroud)

什么时候使用calloc而不是malloc是一个好主意,反之亦然?

Fre*_*son 815

calloc()零初始化缓冲区,同时malloc()保留未初始化的内存.

编辑:

将内存清零可能需要一些时间,因此malloc()如果性能问题,您可能希望使用它.如果初始化内存更重要,请使用calloc().例如,calloc()可能会保存您的电话memset().

  • `calloc`不一定更贵,因为操作系统可以做一些技巧来加速它.我知道FreeBSD,当它获得任何空闲的CPU时间时,使用它来运行一个简单的进程,只是绕过并释放出已释放的内存块,并标记块因此处理带有标志.因此,当你执行`calloc`时,它首先会尝试找到一个这样的预先归零的块,然后将它给你 - 而且很可能会找到一个. (265认同)
  • *alloc变体非常重要 - clear-alloc,memory-alloc,re-alloc. (215认同)
  • 如果要设置在分配的空间中使用的所有内容,请使用malloc().如果您要将部分数据保留为未初始化,请使用calloc() - 将未设置的部分置零是有益的. (41认同)
  • 我倾向于认为,如果您的代码默认情况下由于零启动分配而变得"更安全",那么无论您使用malloc还是calloc,您的代码都不够安全.使用malloc是数据需要初始化的一个很好的指标 - 我只在那些0字节实际有意义的情况下使用calloc.另请注意,calloc不一定按照您对非char类型的方式执行操作.没有人真正使用陷阱表示,或者非IEEE浮点数,但是这并不是没有理由认为你的代码是真正可移植的. (26认同)
  • @SteveJessop"更安全"不是正确的词.我认为"确定性"是更好的术语.更具确定性而不是具有依赖于时序和数据序列的故障的代码将更容易隔离故障.与显式初始化相比,Calloc有时是获得确定性的简单方法. (18认同)
  • 使用calloc()通常可能是一个更安全的赌注,除非你试图优化代码中的每一个最后一点(即便如此,正如其他人所指出的那样,你的努力可能是徒劳的). (3认同)
  • "更安全"可能是一种夸大其词,它更像是"更容易崩溃而不是无声地破坏数据" - 这仍然与代码安全相关(作为最后的防御屏障),并且它确实使调试崩溃变得更容易.也就是说,关于`float`(以及其他所有位0的类型可能并不意味着你认为它们的意思)的非常好的观点. (3认同)
  • 只是添加:calloc添加了另一个防御措施:它通常是针对整数溢出保存的,例如在glibc中它们检查大小乘以元素数量不会超过size_t限制的最大值.通常在调用malloc时不检查溢出,并且按元素的数量乘以元素的大小,但是calloc会为你做,并且如果溢出则调用失败; (实际上,如果您从刚刚通过网络阅读的消息中推断出这些值,则可以利用此功能). (3认同)
  • @dennis,人们提出的观点是,如果您的代码的行为甚至执行路径取决于未初始化内存的内容,那通常就是问题所在。在某些情况下,您可以证明这样做是可以的,例如 http://research.swtch.com/sparse,但这只是个例外。 (2认同)
  • 您可以添加一个关于此的注释:*尽管大多数现代平台都表示空指针,因为所有位都为零,但C标准不保证由`calloc()`分配的数组元素可以安全地假定为初始化为`NULL`指针.* (2认同)

Isa*_*avo 357

一个鲜为人知的区别是,在具有乐观内存分配的操作系统(如Linux)中,返回的指针malloc不会被实际内存支持,直到程序实际触及它为止.

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

例如,请参阅此SO问题,以进一步讨论malloc的行为

  • `calloc`不需要写零.如果分配的块主要由操作系统提供的新零页面组成,则可以保留那些未触及的页面.这当然需要将`calloc`调到操作系统而不是在`malloc`之上的通用库函数.或者,实现者可以使`calloc`将每个字与零进行比较,然后将其归零.这不会节省任何时间,但它可以避免弄脏新页面. (46认同)
  • 如果块是通过`mmap`新匿名页面(或等效页面)获得的,那么所有`dlmalloc`-like实现都会跳过`memset`.通常这种分配用于较大的块,从256k左右开始.我不知道任何与零进行比较的实现,除了我自己写零之前. (10认同)
  • glibc的calloc检查它是否从操作系统获得了新的内存.如果是这样,它知道它不需要写它,因为mmap(...,MAP_ANONYMOUS)返回已经归零的内存. (10认同)
  • @R ..有趣的说明.但在实践中,这种实现是否存在于野外? (3认同)

R..*_*R.. 106

一个经常被忽视的优点calloc是(符合实现)它将有助于保护您免受整数溢出漏洞的影响.相比:

size_t count = get_int32(file);
struct foo *bar = malloc(count * sizeof *bar);
Run Code Online (Sandbox Code Playgroud)

size_t count = get_int32(file);
struct foo *bar = calloc(count, sizeof *bar);
Run Code Online (Sandbox Code Playgroud)

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

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

  • 显然,算术溢出是导致2002年OpenSSH漏洞的原因.来自OpenBSD的好文章介绍了与内存相关的函数的危险:http://undeadly.org/cgi?action = article&sid = 20060330071917 (17认同)
  • @KomradeP.:有趣.可悲的是,您链接的文章在开头就有错误的信息.使用`char`的例子不是**溢出,而是在将结果返回到`char`对象时实现定义的转换. (4认同)
  • @tristopia:如果你的思维方式是"`size_t`是64位,那就没问题了",这是一种有缺陷的思维方式,会导致安全漏洞.`size_t`是一个表示大小的抽象类型,并且没有理由认为32位数字和`size_t`的任意乘积(注意:`sizeof*bar`原则上可以大于2 ^ 32) 64位C实现!)适合`size_t`. (3认同)

t0r*_*kka 35

该文档使calloc看起来像malloc,它只是初始化内存; 这不是主要的区别!calloc的想法是为内存分配禁止写时复制语义.使用calloc分配内存时,它都映射到初始化为零的同一物理页面.当分配的存储器的任何页面被写入物理页面时被分配.这通常用于制作HUGE哈希表,例如,因为哈希的部分是空的,不会被任何额外的内存(页面)支持; 他们高兴地指向单个零初始化页面,甚至可以在进程之间共享.

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

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


AnT*_*AnT 26

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

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

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

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

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


Ash*_*van 21

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

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

  • 第一行答案包含Georg Hager博客的链接. (2认同)

vir*_*rco 11

calloc一般malloc+memset为0

malloc+memset明确使用它通常稍微好一点,特别是当您执行以下操作时:

ptr=malloc(sizeof(Item));
memset(ptr, 0, sizeof(Item));
Run Code Online (Sandbox Code Playgroud)

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

一个值得注意的例外是当你正在做一个非常大的内存块(一些power_of_two千字节)的malloc/calloc时,在这种情况下,可以直接从内核完成分配.由于操作系统内核通常会将出于安全原因而放弃的所有内存归零,因此足够聪明的calloc可能会将其返回并附加归零.再说一遍 - 如果你只是分配你知道的东西很小,那么你可能会更好地使用malloc + memset.

  • glibc calloc有一些聪明才能决定如何最有效地清除返回的块,例如,有时只有部分需要清除,还有一个展开清除最多9*sizeof(size_t).内存是内存,一次清除3个字节不会更快,因为你将使用它来保存`struct foo {char a,b,c; };`.`calloc`总是比`malloc` +`memset`更好,如果你总是要清除整个`malloc`ed区域.`calloc`对size*元素的int溢出进行了仔细但有效的检查. (3认同)

Shi*_*hat 8

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

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

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

例如:如果要为int类型分配10块内存,

data_type ptr = (cast_type *)malloc( sizeof(data_type)*no_of_blocks );
Run Code Online (Sandbox Code Playgroud)

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

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

例如:如果你想为int类型分配10块内存并将所有内容初始化为ZERO,

int *ptr = (int *) malloc(sizeof(int) * 10 );
Run Code Online (Sandbox Code Playgroud)

相似:

如果malloc()和calloc()没有类型化,它们将默认返回void*.


Jai*_*dra 7

有两点不同.
首先,是参数的数量.malloc()采用单个参数(以字节为单位的内存),同时calloc()需要两个参数.
其次,malloc()不初始化分配的内存,同时calloc()将分配的内存初始化为ZERO.

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

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

  • malloc()分配REQUSTED SIZE的单个内存块并返回指向第一个字节的指针.如果找不到请求的内存量,则返回空指针.

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


elm*_*mar 7

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

他们的原型如下:

void *malloc( size_t n);
void *calloc( size_t n, size_t t)
Run Code Online (Sandbox Code Playgroud)

两者的区别主要有两点:

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

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

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

例子:

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));
Run Code Online (Sandbox Code Playgroud)

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

// 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)); 
Run Code Online (Sandbox Code Playgroud)

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


Maj*_*bib 7

块数:
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);      
Run Code Online (Sandbox Code Playgroud)

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

名称含义:
名称的malloc意思是“内存分配”。
这个名字的calloc意思是“连续分配”。


Vip*_*kar 6

标题中calloc()声明的函数<stdlib.h>提供了一些优于malloc()函数的优点.

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


chu*_*ica 5

尚未提及的差异:大小限制

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

void *calloc(size_t nmemb, size_t size);可以分配一下SIZE_MAX*SIZE_MAX.

在具有线性寻址的许多平台中通常不使用此能力.这样的系统限制calloc()nmemb * size <= SIZE_MAX.

考虑一种称为512字节的类型,disk_sector代码想要使用大量扇区.在这里,代码最多只能使用SIZE_MAX/sizeof disk_sector扇区.

size_t count = SIZE_MAX/sizeof disk_sector;
disk_sector *p = malloc(count * sizeof *p);
Run Code Online (Sandbox Code Playgroud)

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

size_t count = something_in_the_range(SIZE_MAX/sizeof disk_sector + 1, SIZE_MAX)
disk_sector *p = calloc(count, sizeof *p);
Run Code Online (Sandbox Code Playgroud)

现在,如果这样的系统可以提供如此大的分配是另一回事.今天大多数人不会.然而它已发生多年,当时SIZE_MAX为65535.鉴于摩尔定律,怀疑这将在2030年左右发生,某些内存模型SIZE_MAX == 4294967295和内存池在100 GBytes中.

  • 通常,size_t将能够保存程序可以处理的最大类型对象的大小.size_t为32位的系统不太可能处理大于4294967295字节的分配,并且能够处理大小分配的系统几乎肯定会使`size_t`大于32位.唯一的问题是,对于产品超过"SIZE_MAX"的值使用`calloc`是否可以依赖于产生零而不是返回指向较小分配的指针. (2认同)
  • 为什么可以容纳超过 4G 的单个分配的实现不将“size_t”定义为“uint64_t”? (2认同)