做与做有什么区别:
1
| ptr = (char **) malloc (MAXELEMS * sizeof(char *)); |
或:
1
| ptr = (char **) calloc (MAXELEMS , sizeof(char*)); |
什么时候在malloc上使用calloc是个好主意,反之亦然?
- 在C中,您不投射malloc族的结果。
- 在C语言中,您可以更一般地将上面的内容写成:ptr = calloc(MAXELEMS, sizeof(*ptr));。
- 关于calloc和malloc+memset vorpus.org/blog/why-do-calloc-exist之间区别的有趣文章
- @在我对网上这么多的回答不满意之后,我也发现了博客。Nathaniel J.Smith的分析应该得到100多分。
- 相关:calloc()是否可以总共分配超过size_max?
calloc()0初始化缓冲区,而malloc()使内存未初始化。
编辑:
清空内存可能需要一些时间,因此如果性能有问题,您可能需要使用malloc()。如果初始化内存更重要,请使用calloc()。例如,calloc()可以为您节省一个对memset()的呼叫。
- *alloc变种是非常容易记忆的-清晰的alloc,内存alloc,re alloc。
- 如果要设置分配空间中使用的所有内容,请使用malloc()。如果要使数据的某些部分未初始化,请使用calloc(),将未设置的部分归零将是有益的。
- calloc不一定更贵,因为操作系统可以做一些技巧来加速它。我知道freebsd,当它得到任何空闲的CPU时间时,会用它来运行一个简单的进程,该进程只是来回移动,将释放的内存块归零,并用一个标志标记这些块从而对进程进行处理。所以当你做calloc时,它首先尝试找到一个这样的预调零块,然后把它给你——很可能它会找到一个。
- 一般来说,使用calloc()可能是更安全的选择,除非您试图优化代码中的每一个小部分(甚至像其他人指出的那样,您的努力可能是徒劳的)。
- 我倾向于认为,如果默认情况下由于零初始化分配而使代码变得"更安全",那么无论使用malloc还是calloc,您的代码都不够安全。使用malloc可以很好地指示数据需要初始化——我只在那些0字节实际上有意义的情况下使用calloc。另外请注意,calloc不一定按照您对非char类型的看法进行操作。没有人真的使用陷阱表示法,或者非ieee浮点数,但这并不是认为代码是真正可移植的理由。
- "更安全"可能是一种夸大,更像是"更可能崩溃,而不是默默无闻地破坏数据"——这仍然与代码安全(作为最后的防御屏障)有关,而且它确实使调试崩溃更容易。也就是说,关于float(以及其他类型的all-bits-0可能不代表您认为它们的含义)的非常好的观点。
- @pavel:char *foo = malloc(foolen); assert(foo && memset(foo,0,foolen));包含调试方面的内容,或者您可以使用除0以外的醒目字节。从安全POV的最后一行来看,最好是链接到清除字节的malloc版本(或者将其设置为引人注目的指针大小的值,该值映射为不可读和不可写),而不是有一个策略来调用calloc?
- @史蒂夫:我喜欢这个主意。我正在考虑编写一个面向调试的简单malloc,有时会在最后一个分配的字节之后使用mmap为每个分配编写一个保护页,并且我可能也会将您的用一个特殊的导致崩溃的指针值填充内存的想法结合起来。
- @Jefromi只是想抱怨"一本关于C的书",它将calloc描述为"分配连续空间"。C代表"邻接",是吗?怪诞的。""清楚"是有道理的。Kernighan认为这意味着清晰,太过quora.com/c-programming-language/…
- 以及Calloc评论的早期来源calloc - allocate and clear memory blockminnie.tuhs.org/cgi-bin/utree.pl?文件=v7m/src/libc/gen/callo‌&诳8203;c.c,摘自Jimbalter的评论:此处stackoverflow.com/a/12555996/50979
- @Stevejessop"更安全"不是正确的词。我认为"确定性"是更好的术语。更具确定性的代码,而不是依赖于时间和数据序列的故障,将更容易隔离故障。与显式初始化相比,calloc有时是获取确定性的简单方法。
- @丹尼斯,人们提出的观点是,如果代码的行为甚至执行路径依赖于未初始化内存的内容,那么这通常是一个问题。在有些情况下,你可以证明这样做是可以的,比如research.swtch.com/sparse,但这是非常例外的。
- @丹尼斯,完全同意。我认为比起非确定性代码,更喜欢确定性代码绝对更好。帕维尔也提出了类似的观点。如果您有一个错误无意中读取了未初始化的内存,您更喜欢它的读取随机数据还是空数据?
- 只需添加:calloc添加了另一个防御措施:它通常是针对整数溢出而保存的,例如在glibc中,他们检查大小乘以元素数量是否超出了大小限制的最大值。通常,当调用malloc并将元素的大小乘以元素的数目时,不检查溢出情况,但calloc会为您检查溢出情况,如果它确实溢出,则调用失败;(实际上,如果您从刚刚通过网络读取的消息中推断出这些值,则可以利用这一点)。
- @为围绕零内存的FreeBSD过程铺平道路。你有什么参考资料吗?我觉得这个想法很有趣…
- 我写了一个关于malloc和安全的人为的场景。如果malloc的内存不为零,那么我把它作为内核。
- @b1kmusic:在操作系统上,使用编译器,使用该版本的编译器,使用指定的编译选项,可能就是这样。重要的事情不要指望它。
- 您可能需要注意:尽管大多数现代平台将空指针表示为所有的零位,但C标准并不保证由calloc()分配的数组元素可以安全地假定为初始化为NULL指针。
- 如果calloc和malloc是连续的,那么是否正确地说分配的内存是连续的?我在这里读到-The only difference between malloc() and calloc() is that, malloc() allocates single block of memory whereas calloc() allocates multiple blocks of memory each of same size and sets all bytes to zero.我认为这是不正确的。不是吗?我相信calloc的名字来源于clear allocated memory,也就是说,当我把它与malloc作比较时,分配的内存是零,这是唯一的区别。
- "可以省去你打电话给memset(..)",有趣的是,哪个更快1。malloc(..)和memset(..)或calloc(..)。
- 我当然可以想象一个alloc后面跟着一个循环,循环到0000个所有字节。在我想要这个结果的地方,仅calloc就更好了。
- @在ddddvide对原始问题的评论中回答的nik lz。原来calloc可以快得多(100倍)。这篇文章读得真不错。还有一个很大的区别——传递两个操作数让calloc错误检查大的分配,其中malloc只会溢出并返回一个比您要求的内存更少的缓冲区。
一个鲜为人知的区别是,在具有乐观内存分配的操作系统中,像Linux一样,malloc返回的指针在程序实际接触到它之前并没有被实际内存支持。
calloc确实接触到内存(它在内存上写零),因此您可以确保操作系统使用实际的RAM(或交换)来支持分配。这也是它慢于malloc的原因(它不仅必须将其归零,操作系统还必须通过交换其他进程来找到合适的内存区域)
如需进一步讨论malloc的行为,请参见本问题。
- calloc不需要写零。如果分配的块主要由操作系统提供的新零页组成,那么它可以保持这些零页不变。当然,这需要将calloc调到操作系统,而不是在malloc之上调到通用库函数。或者,实现人员可以让calloc在将每个单词归零之前将其与零进行比较。这不会节省任何时间,但可以避免弄脏新页面。
- @ R.。有趣的提示。但是在实践中,这种实现在野外存在吗?
- 如果块是通过mmap生成的新匿名页面(或等效页面)获得的,那么所有类似dlmalloc的实现都跳过memset。通常这种分配用于更大的块,从256K左右开始。我不知道有什么实现可以在我自己写零之前对零进行比较。
- omalloc也跳过memset;calloc不需要触摸应用程序尚未使用的任何页面(页面缓存),永远。但是,非常原始的calloc实现是不同的。
- glibc的calloc检查它是否从操作系统获得了新的内存。如果是这样,它知道不需要写它,因为mmap(…,map_anonymous)返回已经归零的内存。
- 在Windows上,VirtualAlloc还分配预调零内存。
- 另外,这取决于分配是否跨越多个虚拟内存页;如果它适合当前页面,那么最常见的情况是,在返回的内存之前有一个竞技场头,必须对其进行修改,从而将页面变为脏状态。(链接的问题讨论了一个跨越多页的非常大的分配)
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,前者可能导致很小的分配和随后的缓冲区溢出。在这种情况下,后者将自动失败,因为无法创建较大的对象。
当然,您可能需要注意不一致的实现,这些实现只是忽略了溢出的可能性…如果这是您目标平台上的一个问题,那么无论如何,您都必须对溢出进行手动测试。
- 显然,算术溢出是2002年造成openssh漏洞的原因。OpenBSD关于内存相关函数的危险的好文章:undeadly.org/cgi?action=文章&sid=20060330071917
- @科姆拉德普:有趣。不幸的是,你链接的文章一开始就有错误的信息。使用char的示例不是溢出,而是在将结果分配回char对象时定义的实现转换。
- 可能只是为了说明原因。因为编译器很可能会对此进行优化。我的编译成这个asm:push 1。
- 您应该注意到在该响应的某个地方,您假定一个32位的size_t,因为当size_t是64位宽时,我看不出问题。
- @Tristopia:关键不是代码在所有实现上都是可利用的,而是没有附加的假设它是不正确的,因此不能正确/可移植使用。
- 问题是有些人(我)只在64位机器上编程。我不得不很长时间地抓我的头,才能知道溢出风险在哪里。如果有人提到size_t的大小,那就很明显了。当然,我现在只在64位机器上开发这是我的问题,但是明显性并不一定和其他机器一样(例如,我非常擅长发现32位心智模型的人的代码中有int溢出)。
- @Tristopia:如果你的思维方式是"size_t是64位的,所以这没问题",那么这是一种有缺陷的思维方式,它会导致安全漏洞。size_t是一种表示大小的抽象类型,没有理由认为32位数字与size_t的任意乘积(注:sizeof *bar原则上在64位C实现上可以大于2^32!)适用于size_t。
- @Patrickschl&252;ter您不仅会遇到32位size_t的问题,如果size_t是16位(或介于两者之间的任何值),您也会遇到问题(这在通常总空间小于64kib的嵌入式微控制器上很常见)。
文档使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的值、指针的值应为空指针值,等等。
不过,从迂腐的角度来看,calloc和memset(..., 0, ...)只保证正确初始化(用零)unsigned char类型的对象。其他所有内容都不能保证正确初始化,并且可能包含所谓的陷阱表示,这会导致未定义的行为。换句话说,对于除unsigned char以外的任何类型,上述所有零位模式都可能表示非法值,即陷阱表示。
后来,在C99标准的技术勘误中,对所有整数类型(这是有意义的)定义了行为。也就是说,在当前的C语言中,只能用calloc和memset(..., 0, ...)初始化整数类型。从C语言的角度来看,在一般情况下,使用它初始化任何其他东西都会导致未定义的行为。
实际上,我们都知道,calloc是有效的,但是您是否愿意使用它(考虑到上面的内容)取决于您。我个人更喜欢完全避免它,而是使用malloc,并执行我自己的初始化。
最后,另一个重要的细节是,需要calloc通过将元素大小乘以元素数量来计算内部的最终块大小。同时,calloc必须注意可能的算术溢出。如果无法正确计算请求的块大小,将导致分配失败(空指针)。同时,您的malloc版本不尝试监视溢出。它将分配一些"不可预测"的内存量,以防发生溢出。
- 根据"另一个重要的细节"段落:这似乎使memset(p, v, n * sizeof type);成为一个问题,因为n * sizeof type可能溢出。我想我需要使用一个for(i=0;i循环来实现健壮的代码。
- 如果有一种标准的方法,代码可以断言一个实现必须使用所有的零位作为空指针(否则拒绝编译),这将很有帮助,因为存在使用其他空指针表示的实现,但它们相对较少;不必在此类实现上运行的代码可以如果可以使用calloc()或memset初始化指针数组,则速度更快。
- vorpus.org/blog/why-do-calloc-exist
- @chux no,如果存在具有n元素的数组,其中元素的大小为sizeof type,则n*sizeof type不能溢出,因为任何对象的最大大小必须小于SIZE_MAX。
- @12431234123412341234123关于数组大小<=SIZE_MAX是真的,但是这里没有数组。从calloc()返回的指针可以指向分配的内存,而不是超过SIZE_MAX的内存。许多实现确实将2个参数的产品限制为calloc()到SIZE_MAX,但是C规范没有施加这个限制。
从一篇关于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稍微好一点,尤其是当您在执行以下操作时:
这更好,因为编译器在编译时就知道sizeof(Item),在大多数情况下,编译器会用最好的零内存指令替换它。另一方面,如果memset发生在calloc中,则分配的参数大小不在calloc代码中编译,而实际的memset经常被调用,它通常会包含逐字节填充直到长边界的代码,然后循环填充sizeof(long)块中的内存,最后通过把剩余的空间填满。即使分配器足够聪明,可以调用一些aligned_memset,它仍然是一个通用循环。
一个值得注意的例外是,当您对一个非常大的内存块执行malloc/calloc操作时(一些功率为2千字节),在这种情况下,可以直接从内核进行分配。由于操作系统内核通常会将它们出于安全原因而释放的所有内存归零,因此,足够智能的calloc可能只会在oud额外归零的情况下返回内存。再次说明——如果你只是分配一些你知道很小的东西,那么你最好在性能方面使用malloc+memset。
- +1提醒您,系统库中功能的一般实现不一定比用户代码中的相同操作更快。
- 还有第二点使calloc()比malloc()慢:大小的乘法。calloc()需要使用通用乘法(如果size_t是64位,即使非常昂贵的64位*64位=64位操作),而malloc()通常具有编译时间常数。
- glibc calloc有一些聪明的方法来决定如何最有效地清除返回的块,例如,有时只需要清除其中的一部分,而且还需要清除9*size of(大小为t)的未展开的块。内存就是内存,一次清除3个字节不会更快,因为这样你就可以用它来保存struct foo { char a,b,c; };。如果你总是要清除整个malloced区域,那么calloc总是比malloc+memset好。calloc也对size*元素中的int溢出进行了仔细而有效的检查。
差异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千兆字节中。
- 一般来说,大小t能够容纳程序可以处理的最大对象的大小。大小为32位的系统不太可能处理大于4294967295字节的分配,而能够处理该大小的分配的系统几乎肯定会使size_t大于32位。唯一的问题是,如果使用的值超过SIZE_MAX的calloc,是否可以依赖于产生零,而不是返回一个指向较小分配的指针。
- 同意您的概括,但是C规范允许calloc()分配超过SIZE_MAX。过去它是用16位的size_t来实现的,随着内存的不断降低,我看不出任何原因,即使它不常见,它也无法继续下去。
- C标准允许代码请求大小超过SIZE_MAX的分配。它当然不要求在任何情况下这样的分配都可能成功;我不确定强制不能处理这种分配的实现必须返回NULL(特别是考虑到某些实现通常使用malloc返回指针来返回o尚未提交的空间,当代码实际尝试使用它时可能不可用)。
- 此外,如果过去可能存在可用寻址范围超过最大可表示整数的系统,我认为这种情况不会再发生,因为这需要数十亿GB的存储容量。即使摩尔定律继续成立,从32位不再足够的点到64位不再足够的点,需要从16位足够的点到32位不够的点花费两倍的时间。
- @supercat"我不确定强制无法处理此类分配的实现必须返回空值"-->兼容的C实现有什么特别的好处,"calloc函数返回空指针或指向分配空间的指针。"它并不免除nmemb * size > SIZE_MAX的数学溢出。
- @Supercat"这种情况再次发生的现实可能性,因为这需要数十亿GB的存储容量"-->超过4G的内存足以考虑使用calloc()的体系结构,该体系结构将返回指向内存>SIZE_MAX (4G-1)的指针。
- 为什么一个能够容纳超过4G的单个分配的实现不能定义size_t到uint64_t?
- 让我们在聊天中继续讨论。
malloc()和calloc()是C标准库中允许动态内存分配的函数,这意味着它们都允许在运行时进行内存分配。
其原型如下:
两者之间主要有两个区别:
两者的返回值都是指向已分配内存块的指针(如果成功)。否则,将返回空值,指示内存分配失败。
例子:
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()。