提问人:user105033 提问时间:10/8/2009 最后编辑:Ninjakannonuser105033 更新时间:2/3/2023 访问量:638320
malloc 和 calloc 的区别?
Difference between malloc and calloc?
问:
doing和有什么区别:
ptr = malloc(MAXELEMS * sizeof(char *));
和:
ptr = calloc(MAXELEMS, sizeof(char*));
什么时候使用是个好主意,反之亦然?calloc
malloc
答:
calloc()
为您提供一个零初始化缓冲区,同时保持内存未初始化状态。malloc()
对于大型分配,主流操作系统下的大多数实现都会从操作系统(例如通过 POSIX 或 Windows )获取已知零页面,因此不需要在用户空间中编写它们。这也是正常从操作系统获取更多页面的方式; 只是利用了操作系统的保证。calloc
mmap(MAP_ANONYMOUS)
VirtualAlloc
malloc
calloc
这意味着内存仍然可以是“干净的”和延迟分配的,并且写入时复制映射到系统范围的零共享物理页。(假设系统具有虚拟内存。例如,在 Linux 上进行性能实验时,效果是显而易见的。calloc
有些编译器甚至可以将 malloc + memset(0) 优化为 calloc,但如果你想将内存归零,最好只在源代码中使用 calloc。(或者,如果您试图预先出错以避免以后出现页面错误,则该优化将挫败您的尝试。
如果你不打算在写入内存之前读取内存,请使用它,这样它就可以(可能)从其内部可用列表中给你脏内存,而不是从操作系统获取新页面。malloc
如果没有操作系统,嵌入式实现可能会将其自身保留为零内存,或者它不是一个花哨的多用户操作系统,可以将页面归零以阻止进程之间的信息泄漏。calloc
calloc
在嵌入式 Linux 上,malloc 可以 mmap(MAP_UNINITIALIZED|MAP_ANONYMOUS),
它仅对某些嵌入式内核启用,因为它在多用户系统上不安全。
评论
calloc
不一定更昂贵,因为操作系统可以做一些技巧来加快它的速度。我知道 FreeBSD 在获得任何空闲的 CPU 时间时,会使用它来运行一个简单的进程,该进程会四处走动并将释放的内存块清零,并用标志标记块,从而标记进程块。因此,当您这样做时,它首先会尝试找到这样的预归零块之一,然后将其提供给您 - 并且很可能会找到一个。calloc
一个鲜为人知的区别是,在具有乐观内存分配的操作系统中,如Linux,在程序实际接触它之前,返回的指针不会得到实际内存的支持。malloc
calloc
确实会触及内存(它会在上面写入零),因此您将确定操作系统正在使用实际 RAM(或交换)支持分配。这也是为什么它比 malloc 慢的原因(它不仅必须将其归零,操作系统还必须通过可能交换其他进程来找到合适的内存区域)
例如,请参阅此 SO 问题,以进一步讨论 malloc 的行为
评论
calloc
不需要写零。如果分配的块主要由操作系统提供的新零页组成,则可以保留这些零页。当然,这需要根据操作系统进行调整,而不是在 之上使用通用库函数。或者,实现者可以在将每个单词归零之前将其与零进行比较。这不会节省任何时间,但可以避免弄脏新页面。calloc
malloc
calloc
dlmalloc
memset
mmap
分配的内存块的大小没有区别。 只是用物理全零位模式填充内存块。在实践中,通常假设位于分配的内存块中的对象具有初始值,就好像它们是用文字初始化的一样,即整数应该具有 的值 ,浮点变量 - 值 ,指针 - 适当的空指针值,等等。calloc
calloc
0
0
0.0
不过,从迂腐的角度来看,(以及 )只能保证正确初始化(零)类型的对象。其他所有内容都不能保证正确初始化,并且可能包含所谓的陷阱表示,这会导致未定义的行为。换言之,对于除上述全零位 patterm 以外的任何类型,patterm 都可能表示非法值,即陷阱表示。calloc
memset(..., 0, ...)
unsigned char
unsigned char
后来,在 C99 标准的技术勘误之一中,为所有整数类型定义了行为(这是有道理的)。也就是说,在当前的 C 语言中,您只能使用 (和) 初始化整数类型。从 C 语言的角度来看,在一般情况下使用它来初始化任何其他内容会导致未定义的行为。calloc
memset(..., 0, ...)
在实践中,正如我们都知道的那样,:),但你是否想使用它(考虑到上述情况)取决于你。我个人更喜欢完全避免它,而是使用并执行我自己的初始化。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
calloc()
SIZE_MAX
一个经常被忽视的优点是(一致的实现)它将有助于保护您免受整数溢出漏洞的侵害。比较: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
size_t
size_t
sizeof *bar
size_t
有两个区别。
首先,是参数的数量。 接受单个参数(以字节为单位所需的内存),而需要两个参数。
其次,不初始化分配的内存,同时将分配的内存初始化为零。malloc()
calloc()
malloc()
calloc()
calloc()
分配一个内存区域,长度将是其参数的乘积。 用零填充内存,并返回指向第一个字节的指针。如果它无法找到足够的空间,它将返回一个指针。calloc
NULL
语法:即ptr_var = calloc(no_of_blocks, size_of_each_block);
ptr_var = calloc(n, s);
malloc()
分配 REQUSTED SIZE 的单个内存块,并返回指向第一个字节的指针。如果它无法找到请求的内存量,则返回一个 null 指针。
语法:该函数采用一个参数,即要分配的字节数,而该函数采用两个参数,一个是元素数,另一个是为每个元素分配的字节数。此外,将分配的空间初始化为零,而不初始化。ptr_var = malloc(Size_in_bytes);
malloc()
calloc()
calloc()
malloc()
与函数相比,标头中声明的函数具有一些优点。calloc()
<stdlib.h>
malloc()
- 它将内存分配为给定大小的多个元素,并且
- 它初始化分配的内存,以便所有位都 零。
文档使 看起来像 ,它只是对内存进行零初始化;这不是主要区别!其思想是抽象出用于内存分配的写入时复制语义。当您用它分配内存时,所有内存都映射到初始化为零的同一物理页。当分配的内存的任何页面写入物理页面时,就会分配一个物理页面。这通常用于制作巨大的哈希表,例如,因为空的哈希部分没有任何额外的内存(页面)支持;他们高兴地指向单个零初始化页面,该页面甚至可以在进程之间共享。calloc
malloc
calloc
calloc
任何对虚拟地址的写入都将映射到一个页面,如果该页面是零页面,则分配另一个物理页面,将零页面复制到该页面,并将控制流返回到客户端进程。这与内存映射文件、虚拟内存等的工作方式相同。它使用分页。
以下是关于该主题的优化故事:http://blogs.fau.de/hager/2007/05/08/benchmarking-fun-with-calloc-and-zero-pages/
摘自 Georg Hager 博客上的一篇文章 Benchmarking fun with calloc() and zero pages
使用 calloc() 分配内存时,请求的内存量不会立即分配。取而代之的是,所有属于内存块的页面都通过一些 MMU 魔术连接到一个包含所有零的页面(下面的链接)。如果只读取这些页面(在基准测试的原始版本中,数组 b、c 和 d 就是如此),则数据是从单个零页面提供的,当然,该页面适合缓存。内存绑定循环内核就这么多。如果一个页面被写入(无论如何),就会发生错误,映射“真实”页面,并将零页面复制到内存中。这被称为写入时复制,这是一种众所周知的优化方法(我甚至在我的 C++ 讲座中多次教授过)。在那之后,零读取技巧不再适用于该页面,这就是为什么在插入(据说是冗余的)初始化循环后性能要低得多的原因。
calloc
一般为0malloc+memset
通常,显式使用会稍微好一些,尤其是当您正在执行以下操作时:malloc+memset
ptr=malloc(sizeof(Item));
memset(ptr, 0, sizeof(Item));
这更好,因为编译器在编译时就知道了,并且在大多数情况下,编译器会用最好的指令替换它,以实现零内存。另一方面,如果发生在 中,则分配的参数大小不会在代码中编译,并且经常调用 real,这通常包含代码来执行逐字节填充直到长边界,然后循环以块填充内存,最后逐字节填充剩余空间。即使分配器足够聪明,可以调用一些,它仍然是一个通用循环。sizeof(Item)
memset
calloc
calloc
memset
sizeof(long)
aligned_memset
一个值得注意的例外是,当您对非常大的内存块(大约 power_of_two KB )执行 malloc/calloc 时,在这种情况下,可以直接从内核进行分配。由于出于安全原因,操作系统内核通常会将其赠送的所有内存清零,因此足够聪明的 calloc 可能会在不进行额外归零的情况下将其返回。再说一遍 - 如果你只是分配一些你知道很小的东西,那么在性能方面,使用 malloc+memset 可能会更好。
评论
calloc()
malloc()
calloc()
size_t
struct foo { char a,b,c; };
calloc
malloc
memset
malloc
calloc
区别1:
malloc()
通常分配内存块,它是初始化的内存段。
calloc()
分配内存块并将所有内存块初始化为 0。
区别2:
如果考虑语法,则只需要 1 个参数。请看以下示例:malloc()
data_type ptr = (cast_type *)malloc( sizeof(data_type)*no_of_blocks );
例如:如果要为 int 类型分配 10 个内存块,
int *ptr = (int *) malloc(sizeof(int) * 10 );
如果考虑语法,则需要 2 个参数。请看以下示例:calloc()
data_type ptr = (cast_type *)calloc(no_of_blocks, (sizeof(data_type)));
例如:如果要为 int 类型分配 10 个内存块并将所有内存块初始化为 ZERO,
int *ptr = (int *) calloc(10, (sizeof(int)));
相似:
如果它们不是类型转换的,则默认情况下将返回 void*!malloc()
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);
现在,这样的系统是否可以提供如此大的分配是另一回事。今天大多数人不会。然而,当 65535 年时,它已经发生了很多年。鉴于摩尔定律,怀疑这将发生在 2030 年左右,某些内存模型和内存池在 100 GB 中。SIZE_MAX
SIZE_MAX == 4294967295
评论
size_t
calloc
SIZE_MAX
calloc()
SIZE_MAX
size_t
SIZE_MAX
NULL
malloc
size_t
uint64_t
malloc()
和 是 C 标准库中的函数,允许动态内存分配,这意味着它们都允许在运行时分配内存。calloc()
它们的原型如下:
void *malloc( size_t n);
void *calloc( size_t n, size_t t)
两者之间主要有两个区别:
行为:分配一个内存块,而不对其进行初始化,从该块读取内容将导致垃圾值。另一方面,分配一个内存块并将其初始化为零,显然读取该块的内容会导致零。
malloc()
calloc()
语法:接受 1 个参数(要分配的大小),并接受 2 个参数(要分配的块数和每个块的大小)。
malloc()
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()
块数:
分配单个请求的内存块,
分配多个请求的内存块malloc()
calloc()
初始化:
- 不清除和初始化分配的内存。
- 将分配的内存初始化为零。malloc()
calloc()
速度:
很快。
比 malloc() 慢。malloc()
calloc()
参数和语法:接受 1 个参数:malloc()
字节
- 要分配的字节数
calloc()
接受 2 个参数:
长度
- 要分配的内存块数
字节
- 要在每个内存块上分配的字节数
void *malloc(size_t bytes);
void *calloc(size_t length, size_t bytes);
内存分配方式:
该函数从可用堆中分配所需“大小”的内存。
该函数分配的内存大小等于 'num *size'。malloc
calloc
名称的含义:
名称的意思是“内存分配”。
该名称的意思是“连续分配”。malloc
calloc
评论
和分配内存,但将所有位初始化为零,而没有。malloc
calloc
calloc
malloc
Calloc 可以说等价于 0 的 malloc +(其中 memset 将指定的内存位设置为零)。memset
因此,如果不需要初始化为零,那么使用 malloc 可能会更快。
评论
malloc
族的结果ptr = calloc(MAXELEMS, sizeof(*ptr));