两者之间有什么区别:
ptr = (char **) malloc (MAXELEMS * sizeof(char *));
要么:
ptr = (char **) calloc (MAXELEMS, sizeof(char*));
什么时候在malloc上使用calloc是个好主意,反之亦然?
ptr = calloc(MAXELEMS, sizeof(*ptr));
两者之间有什么区别:
ptr = (char **) malloc (MAXELEMS * sizeof(char *));
要么:
ptr = (char **) calloc (MAXELEMS, sizeof(char*));
什么时候在malloc上使用calloc是个好主意,反之亦然?
ptr = calloc(MAXELEMS, sizeof(*ptr));
Answers:
calloc()
给您一个零初始化的缓冲区,而未初始化malloc()
的内存。
对于较大的分配,calloc
主流操作系统下的大多数实现都将从操作系统(例如,通过POSIX mmap(MAP_ANONYMOUS)
或Windows VirtualAlloc
)获得已知为零的页面,因此无需在用户空间中写入它们。这也是正常情况下malloc
从操作系统获取更多页面的方式。calloc
只是利用了操作系统的保证。
这意味着calloc
内存仍然可以“清理”和延迟分配,并且写时复制映射到系统范围的零共享物理页。(假定系统具有虚拟内存。)
一些编译器甚至可以为您优化malloc + memset(0)到calloc中,但是如果您希望将内存读取为,则应显式使用calloc 0
。
如果您不打算在写入之前读取内存,请使用它,malloc
以便(可能)从其内部空闲列表中为您提供脏内存,而不是从操作系统中获取新页面。(或者不是将空闲列表上的内存块清零以进行少量分配)。
如果没有操作系统,或者不是花哨的多用户操作系统将页面清零以阻止进程之间的信息泄漏,则的嵌入式实现calloc
可能会将其calloc
自身留给零内存。
在嵌入式Linux上,可以对malloc mmap(MAP_UNINITIALIZED|MAP_ANONYMOUS)
启用,因为仅在某些嵌入式内核中启用了malloc ,因为它在多用户系统上不安全。
calloc
不一定更贵,因为OS可以采取一些技巧来加快速度。我知道FreeBSD在获得空闲的CPU时间时,会使用它来运行一个简单的过程,该过程会四处循环,并将释放的内存块清零,并使用标志将这些块标记为进程。因此,当您这样做时calloc
,它首先尝试找到这样的预调零块之一,然后将其提供给您-而且很可能会找到一个。
一个鲜为人知的区别是,在具有乐观内存分配的操作系统(例如Linux)中,malloc
直到程序真正接触到它之后,返回的指针才由实内存支持。
calloc
确实确实触及了内存(它在内存上写了零),因此您可以确定操作系统正在支持具有实际RAM(或交换)的分配。这也是为什么它比malloc慢的原因(不仅必须将其清零,而且操作系统还必须通过交换其他进程来找到合适的内存区域)
例如,请参见此SO问题以进一步讨论malloc的行为
calloc
不需要写零。如果分配的块主要由操作系统提供的新零页组成,则可以使那些原样保留。当然,这需要calloc
调整到操作系统,而不是在之上的通用库函数malloc
。或者,实现者可以calloc
将每个单词与零进行比较,然后再将其清零。这不会节省任何时间,但可以避免弄脏新页面。
dlmalloc
类似的实现都将跳过。通常,这种分配用于较大的块,从256k左右开始。我不知道有什么实现可以在我自己写零之前先对零进行比较。memset
mmap
一个经常被忽略的优点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
。在这种情况下,后者将自动失败,因为无法创建较大的对象。
当然,您可能需要寻找不符合标准的实现,而这些实现只是忽略了溢出的可能性...如果您所针对的平台存在此问题,则无论如何都必须手动进行溢出测试。
char
是未分配的结果返回到一个当溢出而是实现定义的转换char
的对象。
size_t
是64位的,那没问题”,那是一种有缺陷的思维方式,将导致安全漏洞。size_t
是代表大小的抽象类型,没有理由认为32位数字和a size_t
(注意:sizeof *bar
在64位C实现上原则上可以大于2 ^ 32!)的任意乘积适合size_t
。
文档使calloc看起来像malloc,它只是对内存进行零初始化。这不是主要区别!calloc的想法是抽象用于存储分配的写时复制语义。当您使用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
上述全零位patterm 以外的任何类型,都可能表示非法值,陷阱表示。
后来,在C99的技术勘误中之一,为所有整数类型定义了行为(这是有道理的)。即,在形式上,在当前的C语言中,您只能使用calloc
(和memset(..., 0, ...)
)初始化整数类型。从C语言的角度来看,一般情况下使用它来初始化其他任何东西都会导致未定义的行为。
实际上,calloc
众所周知,)是可行的,但是,是否要使用它(考虑以上内容)则取决于您。我个人更喜欢完全避免使用它,malloc
而是使用它并执行自己的初始化。
最后,另一个重要的细节是calloc
在内部通过将元素大小乘以元素数来计算最终块大小。这样做时,calloc
必须注意可能的算术溢出。如果无法正确计算所请求的块大小,将导致分配失败(空指针)。同时,您的malloc
版本不会尝试监视溢出。如果发生溢出,它将分配一些“不可预测的”内存。
memset(p, v, n * sizeof type);
问题,因为n * sizeof type
可能会溢出。猜猜我需要使用一个for(i=0;i<n;i++) p[i]=v;
循环来生成健壮的代码。
n
存在带有元素的数组且元素具有大小sizeof type
,则n*sizeof type
不会溢出,因为任何对象的最大大小必须小于SIZE_MAX
。
SIZE_MAX
,但此处没有数组。从返回的指针calloc()
可以指向已分配的内存,其数目超过了SIZE_MAX
。许多实现的确将2个args的乘积限制calloc()
为SIZE_MAX
,但是C规范没有强加该限制。
摘自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
,而通常会调用real ,这通常会包含代码来逐字节填充直到长边界,而不是循环填充sizeof(long)
逐块存储,最后逐字节填充剩余空间。即使分配器足够聪明,可以调用某些分配器,aligned_memset
它仍将是一个通用循环。
一个值得注意的例外是,当您对非常大的内存块(某些power_of_两千字节)执行malloc / calloc时,这种情况下可以直接从内核完成分配。由于操作系统内核通常会出于安全原因将它们释放的所有内存清零,因此足够聪明的calloc可能会在没有额外清零的情况下将其返回。再说一次-如果您只是分配一些您知道的很小的东西,那么在性能方面使用malloc + memset可能会更好。
calloc()
慢于malloc()
:大小相乘。calloc()
需要使用通用乘法(如果size_t
是64位,即使是非常昂贵的64位* 64位= 64位操作),而malloc()通常具有编译时间常数。
struct foo { char a,b,c; };
。 如果您总是要清除整个ed区域,calloc
它总是比malloc
+ 好。 也对大小*元素的int溢出进行了仔细但有效的检查。memset
malloc
calloc
差异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()
需要一个参数(以字节为单位的内存),而calloc()
需要两个参数。
其次,malloc()
不初始化分配的内存,而calloc()
将分配的内存初始化为零。
calloc()
分配一个内存区域,长度将为其参数的乘积。calloc
用零填充内存,并返回指向第一个字节的指针。如果找不到足够的空间,它将返回一个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()
不会初始化为零。
该calloc()
是在声明的函数<stdlib.h>
头提供了几个优点,在malloc()
功能。
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 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));
calloc()
使用malloc()
和可以实现相同的功能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));
请注意,malloc()
最好使用calloc()
它,因为它更快。如果需要零值初始化,请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
。
考虑一种称为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 GB。
size_t
大于32位。唯一的问题是,是否可以依靠使用calloc
乘积超过的值SIZE_MAX
产生零,而不是返回指向较小分配的指针。
calloc()
分配超出的值SIZE_MAX
。它过去曾经发生在16位,size_t
并且随着内存的持续廉价,我认为没有理由即使它不常见也不会发生。
SIZE_MAX
。当然,它不要求在任何情况下这种分配都可以成功;我不确定强制执行无法处理此类分配的实现是否会带来任何特别的好处NULL
(尤其是鉴于某些实现通常具有malloc
尚未提交且在代码实际尝试使用时可能不可用的空间的返回指针)它)。
size_t
为uint64_t
?
块数:
malloc()分配所请求内存的单个块,
calloc()分配所请求内存的多个块
初始化:
malloc()-不清除并初始化分配的内存。
calloc()-将分配的内存初始化为零。
速度:
malloc()很快。
calloc()比malloc()慢。
参数和语法:
malloc()接受1个参数:
个字节
calloc()接受2个参数:
长度
void *malloc(size_t bytes);
void *calloc(size_t length, size_t bytes);
内存分配方式:
malloc函数从可用堆中分配所需“大小”的内存。
calloc函数分配的内存等于'num * size'的大小。
名称上的含义:
名称malloc表示“内存分配”。
名称calloc表示“连续分配”。
malloc
家庭的结果