为啥 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”?的主要内容,如果未能解决你的问题,请参考以下文章

为啥标准 C++ 库中没有“int pow(int base, int exponent)”?

为啥 ANSI C 没有命名空间?

为啥没有返回语句时没有编译器错误?

C语言为啥可以重写标准库函数?

为啥 C++ 分配器中没有重新分配功能?

为啥 C++ 标准库中没有 SIMD 功能?