malloc和calloc之间的区别?
做什么有什么区别:
ptr = (char **) malloc (MAXELEMS * sizeof(char *));
要么:
ptr = (char **) calloc (MAXELEMS, sizeof(char*));
什么时候使用calloc over malloc是一个好主意,反之亦然?
calloc()
初始化缓冲区,而malloc()
使内存未初始化。
编辑:
清零内存可能需要一点时间,所以如果性能是个问题,您可能需要使用malloc()
。 如果初始化内存更重要,请使用calloc()
。 例如, calloc()
可能会保存您对memset()
的调用。
一个鲜为人知的区别是,在内存分配比较乐观的操作系统中,像Linux一样, malloc
返回的指针在程序实际触及它之前不会被真实存储器支持。
calloc
的确会触及内存(它会写入零),因此您可以确定操作系统正在使用实际的内存(或交换)进行分配。 这也是为什么它比malloc慢(不仅需要将其清零,操作系统还必须找到一个合适的内存区域,可能会将其他进程替换掉)
关于malloc行为的进一步讨论,请看这个问题
calloc
一个经常被忽视的优点是(符合性的实现)它将有助于保护你免受整数溢出漏洞的攻击。 比较:
size_t count = get_int32(file); struct foo *bar = malloc(count * sizeof *bar);
与
size_t count = get_int32(file); struct foo *bar = calloc(count, sizeof *bar);
如果count
大于SIZE_MAX/sizeof *bar
,那么前者可能会导致微小的分配和随后的缓冲区溢出。 后者将在这种情况下自动失败,因为大的对象不能被创建。
当然,您可能需要寻找不符合实现的实现,这些实现只是忽略了溢出的可能性。如果这是您定位平台上的一个问题,那么您必须对溢出进行手动测试。
分配的内存块大小没有区别。 calloc
只是用物理的全零位模式填充内存块。 在实践中,通常假设位于分配有calloc
的内存块中的对象具有初始值,就好像它们是用0
来初始化的,即整数应该具有值0
,浮点变量 – 值为0.0
,指针 – 适当的空指针值,等等。
尽管从迂腐的角度来看, calloc
(以及memset(..., 0, ...)
)只能保证正确初始化(带零) unsigned char
类型的对象。 其他一切都不能保证被正确初始化,并可能包含所谓的陷阱表示 ,这会导致未定义的行为。 换句话说,对于除unsigned char
以外的任何类型,前述的全零位patterm可能代表一个非法的值,陷阱表示。
后来,在C99标准的技术勘误之一中,对于所有整数类型(这是有道理的)定义了行为。 也就是说,在当前的C语言中,只能用calloc
(和memset(..., 0, ...)
)初始化整数类型。 从C语言的角度来看,在一般情况下使用它来初始化其他任何事情都会导致未定义的行为。
在实践中,我们都知道calloc
有效的,但是,是否要使用它(考虑上述)取决于你。 我个人更喜欢完全避免它,而是使用malloc
来执行我自己的初始化。
最后,另一个重要的细节是calloc
需要在内部计算最终的块大小,通过元素大小乘以元素的数量。 在这样做的时候, calloc
必须注意可能的算术溢出。 如果所请求的块大小无法正确计算,将导致分配不成功(空指针)。 同时,您的malloc
版本不会尝试监视溢出。 如果发生溢出,它将分配一些“不可预知”的内存量。
文档使calloc看起来像malloc,它只是零初始化内存; 这不是主要的区别! calloc的思想是为存储器分配提供copy-on-write语义。 当你用calloc分配内存时,它全部映射到初始化为零的相同物理页面。 当分配的内存中的任何页面被写入物理页面时被分配。 这通常被用来制作巨大的散列表,例如,由于散列的散列部分不被任何额外的存储器(页面)支持; 他们高兴地指向单个零初始化页面,甚至可以在进程之间共享。
任何对虚拟地址的写入都被映射到一个页面,如果该页面是零页面,则分配另一个物理页面,在那里复制零页面,并将控制流程返回给客户端进程。 这工作方式相同的内存映射文件,虚拟内存等工作..它使用分页。
以下是关于该主题的一个优化故事: http : //blogs.fau.de/hager/2007/05/08/benchmarking-fun-with-calloc-and-zero-pages/
从一篇文章与 乔治·海格博客上的calloc()和零页面进行基准对比
当使用calloc()分配内存时,请求的内存量不会立即分配。 相反,属于内存块的所有页面通过一些MMU魔术(下面的链接)连接到包含全零的单个页面。 如果只读取这些页面(对于原始版本的基准测试中的数组b,c和d,则为真),数据是从单个零页面提供的,当然这些页面可以放入缓存中。 对于内存限制的循环内核来说非常重要。 如果页面被写入(无论如何),则发生错误,“真实”页面被映射,并且零页被复制到存储器。 这被称为copy-on-write,一个众所周知的优化方法(我甚至在我的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性能明智。
malloc()
分配给定大小的内存块(以字节为单位),并返回一个指向块开始的指针。
void *malloc(size_t size);
malloc()
不初始化分配的内存。
calloc()
分配内存,并初始化分配内存到所有位零。
void *calloc(size_t num, size_t size);
有两个区别。
首先,是在论点的数量。 malloc()
需要一个参数(字节所需的内存),而calloc()
需要两个参数。
其次, malloc()
不初始化分配的内存,而calloc()
初始化分配的内存到ZERO。
-
calloc()
分配一个内存区域,长度将是其参数的乘积。calloc
用ZERO填充内存并返回一个指向第一个字节的指针。 如果找不到足够的空间,则返回NULL
指针。
语法: ptr_var=(cast_type *)calloc(no_of_blocks , size_of_each_block);
即ptr_var=(type *)calloc(n,s);
-
malloc()
分配一个REQUSTED SIZE内存块并返回一个指向第一个字节的指针。 如果找不到应有的内存量,它将返回一个空指针。
语法: ptr_var=(cast_type *)malloc(Size_in_bytes);
malloc()
函数有一个参数,它是要分配的字节数,而calloc()
函数有两个参数,一个是元素的数量,另一个是分配给每个元素的字节数。 此外, calloc()
将分配的空间初始化为零,而malloc()
不初始化。
差异1:malloc()通常分配内存块,它是初始化的内存段。 calloc()分配内存块并将所有内存块初始化为0。
差异2:如果考虑malloc()语法,则只需要1个参数。 考虑下面的例子:
ptr data_type =(cast_type *)malloc(sizeof(data_type)* no_of_blocks);
例如:如果你想为int类型分配10块内存,
int *ptr = (int *) malloc(sizeof(int) * 10 );
如果您考虑calloc()语法,则需要2个参数。 考虑下面的例子:
ptr data_type =(cast_type *)calloc(no_of_blocks,(sizeof(data_type)));
例如:如果要为int类型分配10个内存块,并将所有内存初始化为零,
int *ptr = (int *) calloc(10, (sizeof(int)));
相似:
如果malloc()和calloc()不是键入类型,则它们将默认返回void *。
在<stdlib.h>
头文件中声明的calloc()
函数比malloc()
函数提供了一些优点。
- 它将内存分配给一定数量的元素,并且
- 它初始化分配的内存,使所有的位都为零。
尚未提及的差异: 大小限制
void *malloc(size_t size)
只能分配到SIZE_MAX
。
void *calloc(size_t nmemb, size_t size);
可以分配约SIZE_MAX*SIZE_MAX
。
线性寻址的许多平台并不经常使用这种功能。 这样的系统用nmemb * size <= SIZE_MAX
限制calloc()
。
考虑一个512字节的类型,称为disk_sector
,代码想要使用大量的扇区。 在这里,代码最多只能使用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个GBytes的内存池。
malloc():分配所请求的字节大小,并返回指针分配空间的第一个字节
calloc():为数组元素分配空间,初始化为零,然后返回一个指向内存的指针
malloc和calloc之间的主要区别是:
- malloc代表内存分配,而calloc代表连续分配 。
-
malloc只接受一个参数 ,即块的大小,而calloc则需要两个参数 ,要分配的块的数量和每个块的大小。
ptr =(cast-type *)malloc(byte-size)// malloc
ptr =(cast-type *)calloc(块号,块大小); // calloc
-
malloc不执行内存初始化,所有地址都存储垃圾值,而calloc执行内存初始化,地址初始化为零或空值 。
名称malloc和calloc()是动态分配内存的库函数。
这意味着内存是在运行时(程序的执行)从堆段分配的。
初始化:malloc()分配给定大小的内存块(以字节为单位),并返回一个指向块开始的指针。
> malloc() doesn't initialize the allocated memory. If we try to access the content of memory block then we'll get garbage values. void * > malloc( size_t size ); > calloc() allocates the memory and also initializes the allocates memory block to zero. If we try to access the content of these blocks then we'll get 0. > void * calloc( size_t num, size_t size );
一些参数:与malloc()不同,calloc()有两个参数:1)要分配的块数。 2)每个块的大小。
最重要的 :
除非我们需要零初始化,因为malloc比calloc快,否则最好使用malloc over calloc。 所以如果我们只想复制一些东西或者做一些不需要用零填充的块,那么malloc就是一个更好的选择。
char *ptr = (char *) malloc (n * sizeof(char));
只是分配n bytes
的内存而没有任何初始化(即;那些内存字节将包含任何垃圾值)。
然而,除了malloc()
函数以外,c中的calloc()
方法对所有占用的内存字节进行初始化为0
。
malloc()接受一个参数,而calloc()接受两个参数。
其次,malloc()不初始化分配的内存,而calloc()将分配的内存初始化为ZERO。 malloc和calloc在C语言中用于动态内存分配,它们动态地获得内存块。