为什么C标准中没有"recalloc"函数?

17

大家都知道:

  • realloc 重新分配一个已经存在的内存块或者将它复制到更大的内存块。
  • calloc 确保内存被清零,避免算术溢出,并且通常用于大型数组。

C标准为什么不提供一个像下面这样同时结合了这两者的函数呢?

void *recalloc(void *ptr, size_t num, size_t size);

如果能够调整巨大哈希表或自定义内存池的大小,这岂不是非常有用?


7
如果你只是要将所有内容都归零,那么调整大小就没有太大意义了 - 只需释放旧内存块,然后调用calloc函数申请一个新内存块。 - Paul R
12
@PaulR:我想它只会将新内存清零(或者更确切地说确保它被清零)。 - Matt
5
标准库的重点不在于提供丰富而酷炫的功能,而是提供一个基础的构建块集合,你可以使用这些构建块来构建自己的酷炫功能。因此,你提出的 recalloc 功能很容易编写,因此不应该由标准库提供。 - abelenky
3
考虑到所有情况,这个提议对我来说似乎很合理。 - M.M
4
calloc()有一个malloc()所没有的特性:在像DOS这样的古老系统中,能够分配比SIZE_MAX大的数组。因此,即使size_t是16位的,代码也可以使用calloc(60000u, sizeof(double))。我一直在想这是否符合C标准,但看起来是正确的。 - chux - Reinstate Monica
显示剩余11条评论
2个回答

16

通常在C中,标准库的目的不是提供一组丰富而酷炫的函数,而是提供一个基本的构建块集,您可以使用这些构建块构建自己的酷炫函数。

您对recalloc的建议很容易编写,因此不应该由标准库提供。

其他语言采用了不同的方法:C#和Java具有超级丰富的库,即使是复杂的任务也变得轻松。但它们带来了巨大的开销。 C的开销很小,这有助于使其适用于各种嵌入式设备。


5
若要在不调用“memset”的情况下高效编写此函数,您必须了解有关系统虚拟内存的全部信息。 - Matt
9
@abelenky的意思是,有些操作系统从一个不同于malloc页面池的地方(如果可能)获取calloc页面,并且calloc使用懒惰分配和写时复制技术从全零页中复制。这就是为什么在Linux上使用calloc比使用malloc更快(而且比使用malloc后再用memset更快)。 - M.M
4
你需要遍历整个剩余的块,而不能依赖于预先清零的写时复制内存。 - Matt
我认为在K&R添加malloc到语言时,那个想法还没有被发明 :) - M.M
3
@MattMcNabb说:size_t以及许多其他人都使用的东西也没有。 - Matt
4
我不认为提出的recalloc版本很容易编写,即使使用memset:为了使recalloc作为零扩展副本工作,您必须跟踪内存的初始大小。在我看来,realloc背后的理由是为了减轻用户对分配内存大小的跟踪。如果标准库只提供基本函数,它最初就不会引入realloc,因为可以很容易地用条件malloc-copy-free语句表达它。 - aprelev

5

我想您只是想将数组的新部分清零:

并不是每个内存分配器都知道您在数组中使用了多少内存。例如,如果我执行以下操作:

char* foo = malloc(1);

foo现在指向至少1个字节大小的一块内存。但大多数分配器将分配比1个字节更多的内存(例如8个字节,以保持对齐)。

其他分配也可能发生这种情况。内存分配器将分配至少您请求的内存大小,但通常会多分配一点。

而正是这个“多分配一点”的部分使事情变得复杂(除了其他使此难以处理的因素)。因为我们不知道它是否是有用的内存。如果它只是填充,而您使用recalloc函数重新分配它,并且分配器没有将其清零,则现在您就有了具有某些非零值的“新”内存。

例如,如果我使用recallocfoo重定向到至少2个字节的新缓冲区,那么额外的一个字节会被清零吗?还是不会?应该清零,但请注意,原始分配给我们8个字节,因此重新分配不会分配任何新内存。就分配器而言,它不需要将任何内存清零(因为没有“新”内存需要清零)。这可能会导致我们代码中的严重错误。


1
@Cornstalks:那么就必须有一个警告,它只能与先前由calloc或其自身分配的内存正常工作。 - Matt
1
@Matt 考虑对 100001 进行calloc重新分配,然后是 100008,并重复此过程。每次增加都需要清零7个字节。 - chux - Reinstate Monica
2
@Matt:你可以这样做,但现在你正在把自己推向一个奇怪的角落,而recalloc函数的实用性正在降低。你可以通过添加越来越多的任意限制来使其工作,但在某个时候(很快就会到来),要求用户跟踪内存并在realloc后清零新内存只是更容易的选择。 - Cornstalks
2
@Matt 我的建议是更清晰地使用calloc重新分配100001,然后是100008,再回到100001,然后重复这个过程。真正的块大小从未改变。但我确实看到在减小时,填充字节可能为零 - 因此不需要保留先前的请求大小。感谢你的挑战。 - chux - Reinstate Monica
2
@Cornstalks 就标准而言,并没有额外的字节。分配额外的字节来“保持对齐”是特定实现可能会做的事情,就像它可能会跟踪请求和发行的内存一样。正如我所说,recalloc将强制实现清除这些额外的字节,当然。就像某些架构上的对齐要求迫使实现首先分配这些额外的字节一样。 - aprelev
显示剩余15条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接