为啥 C 标准中没有“recalloc”?
Posted
技术标签:
【中文标题】为啥 C 标准中没有“recalloc”?【英文标题】:Why is there no "recalloc" in the C standard?为什么 C 标准中没有“recalloc”? 【发布时间】:2015-04-13 18:43:22 【问题描述】:每个人都知道:
realloc
调整现有内存块的大小或将其复制到更大的块中。
calloc
确保内存清零并防止算术溢出,通常适用于大型数组。
为什么 C 标准不提供像下面这样结合上述两者的函数?
void *recalloc(void *ptr, size_t num, size_t size);
它对于调整巨大的哈希表或自定义内存池的大小不是很有用吗?
【问题讨论】:
如果您只是将所有内容归零,那么调整大小没有什么意义 - 只需释放旧块,然后调用新块。 @PaulR:大概它只会将新内存清零(或者更确切地说,确保清零)。 标准库的重点不是提供丰富的炫酷功能。它提供了一组基本的构建块,您可以从中构建自己的酷功能。你的recalloc提案写起来很简单,因此,标准库不应该提供。 对我来说似乎是一个足够公平的提议,考虑所有因素calloc()
有另一个 malloc()
没有的特性:在像 DOS 这样的神秘系统中:分配大于 SIZE_MAX
的数组的能力。因此代码可以calloc(60000u, sizeof (double))
,即使size_t
是16 位。我想知道这是否符合 C 规范 - 但它似乎是正确的。
【参考方案1】:
我假设您只想将数组的新部分归零:
并非每个内存分配器都知道您在数组中使用了多少内存。例如,如果我这样做:
char* foo = malloc(1);
foo
现在指向至少一块 1 字节大的内存。但是大多数分配器会分配比 1 个字节多得多的字节(例如,8 个字节,以保持对齐)。
这也可能发生在其他分配中。内存分配器将至少分配您请求的内存,但通常只是多一点。
正是这个“稍微多一点”的部分把事情搞砸了(除了其他让事情变得困难的因素之外)。因为我们不知道它是否是有用的内存。如果它只是填充,而您 recalloc
它,并且分配器没有将它归零,那么您现在拥有“新”内存,其中包含一些非零值。
例如,如果我 recalloc
foo
让它指向一个至少 2 字节大的新缓冲区会怎样。那个额外的字节会被清零吗?或不?应该是,但请注意,原始分配给了我们 8 个字节,因此重新分配不会分配任何新内存。就分配器而言,它不需要将任何内存归零(因为没有“新”内存归零)。这可能会导致我们的代码出现严重错误。
【讨论】:
这就是为什么这样的函数只对大块内存有用。 @Cornstalks:那么必须要注意的是,它只适用于之前由calloc
或它自己分配的内存。
@Matt:你可以这样做,但现在你把自己画到了一个奇怪的角落,这个recalloc
函数的用处正在下降。您可以通过添加越来越多的任意限制来使其工作,但在某些时候(越早越好),要求用户跟踪内存并在realloc
之后将新内存清零会更容易。跨度>
@Matt 我的建议 - 更清楚的是 - 是 100001 的 calloc-re-allocation,然后是 100008,然后回到 100001,然后重复。真正的块大小从未改变。但我确实看到,在减少时,填充字节可能为零 - 因此否定了保持先前请求大小的需要。感谢您的挑战。
@Cornstalks 就标准而言,没有额外的字节。分配额外的字节来“保持对齐”是特定实现可能会做的事情,就像它可能会跟踪请求的内存和发出的内存一样。正如我所说,recalloc
将强制实现清除那些额外的字节,当然。就像某些架构上的对齐要求迫使实现首先分配那些额外的字节一样。【参考方案2】:
通常在 C 中,标准库的重点是不提供丰富的酷函数集。它是提供一套基本的构建块,您可以从中构建自己的酷功能。
您对recalloc
的建议写起来很简单,因此标准库不应该提供。
其他语言采用不同的方法:C# 和 Java 拥有超级丰富的库,可以让复杂的任务变得微不足道。但它们带来了巨大的开销。 C 的开销最小,这有助于使其可移植到各种嵌入式设备。
【讨论】:
您必须了解系统虚拟内存的所有信息才能高效地编写这样的函数,而无需调用memset
。
@abelenky 他的意思是,某些操作系统从与malloc
页面不同的池中获取calloc
页面(如果可能),并且calloc 使用惰性分配和写时复制从全为零的页面。这就是为什么 Linux 上的 calloc
可以比 malloc
快(并且比 malloc
后跟 memset
快得多)。
@abelenky:您必须遍历整个块的其余部分,而不是依赖预置零的写时复制内存。
@MattMcNabb:没有size_t
,还有很多其他人人都在用的东西。
@abelenky 我不会说recalloc
的提议版本编写起来很简单,即使使用memset
:对于recalloc
作为零扩展副本,您必须保留跟踪内存的初始大小。恕我直言,realloc
背后的基本原理是让用户不必跟踪分配的内存大小。如果标准库只是提供必要的功能,那么一开始就不会引入realloc
,因为它可以很容易地用条件malloc-copy-free语句来表达。以上是关于为啥 C 标准中没有“recalloc”?的主要内容,如果未能解决你的问题,请参考以下文章