我可以假设调用较小大小的 realloc 会释放其余部分吗? [复制]

Posted

技术标签:

【中文标题】我可以假设调用较小大小的 realloc 会释放其余部分吗? [复制]【英文标题】:Can I assume that calling realloc with a smaller size will free the remainder? [duplicate] 【发布时间】:2012-03-23 10:12:10 【问题描述】:

让我们考虑一下这段非常短的 sn-p 代码:

#include <stdlib.h>

int main()

    char* a = malloc(20000);
    char* b = realloc(a, 5);

    free(b);
    return 0;

阅读 realloc 的手册页后,我不完全确定第二行是否会导致 19995 额外字节被释放。引用手册页:The realloc() function changes the size of the memory block pointed to by ptr to size bytes.,但根据该定义,我可以确定其余部分将被释放吗?

我的意思是,b 指向的块肯定包含 5 个空闲字节,所以对于一个懒惰的遵从分配器来说,不为 realloc 行做任何事情就足够了吗?

注意:我使用的分配器似乎释放了 19995 个额外字节,正如 valgrind 在注释掉 free(b) 行时所示:

==4457== HEAP SUMMARY:
==4457==     in use at exit: 5 bytes in 1 blocks
==4457==   total heap usage: 2 allocs, 1 frees, 20,005 bytes allocated

【问题讨论】:

标头&lt;malloc.h&gt; 未由标准定义:更喜欢使用&lt;stdlib.h&gt;。同样从malloc(或realloc)强制转换返回值没有任何用处,并且可能隐藏错误(void*int 的表示不同)编译器否则会捕获. "The size of the memory block pointed to by the ptr parameter is changed to the size bytes, expanding or reducing the amount of memory available in the block." @pmg 好的,我不知道。我会改变我的sn-p 请注意,Valgrind 并不反映您的普通分配器的性能,因为它实际上用 Valgrind 提供的分配器替换了您通常使用的分配器。 【参考方案1】:

是的,如果可以分配新对象,则由 C 标准保证。

(C99, 7.20.3.4p2) "realloc 函数释放 ptr 指向的旧对象,并返回一个指向具有 size 指定大小的新对象的指针。"

【讨论】:

The C99 draft 从未提及任何反对 realloc 在旧大小等于新大小时什么都不做的事情。我猜所有的实现都违反了它? C99 7.20.3.4 §4:realloc 函数返回一个指向新对象的指针(可能与指向旧对象的指针具有相同的值)【参考方案2】:

是的——如果成功的话。

您的代码 sn-p 显示了一个众所周知的恶意错误:

char* b = (char*) realloc(a, 5);

如果成功,之前分配给a 的内存将被释放,b 将指向可能与原始块重叠也可能不重叠的 5 字节内存。

然而,如果调用失败,b 将是null,但a 仍将指向它的原始内存,这仍然是有效的。在这种情况下,您需要free(a) 才能释放内存。

如果你使用常见的(危险的)成语,那就更糟了:

a = realloc(a, NEW_SIZE);     // Don't do this!

如果对realloc 的调用失败,a 将是null,并且它的原始内存将被孤立,使其无法挽回地丢失,直到您的程序退出。

【讨论】:

你是对的。我见过 cppcheck 抱怨这个。我猜这是考试的杀手锏,但真的,这曾经,曾经发生过吗? @qdii:在嵌入式世界中,所有的赌注都没有了;此外,操作系统可能会实施一些限制,例如通过ulimit 对虚拟内存空间进行限制 是的,这确实发生在现实世界中。为什么你认为当你用完内存(在没有交换的系统上)时,这么多糟糕的软件会崩溃,而不是给你一个消息说它由于内存不足而无法执行请求的操作?【参考方案3】:

这取决于您的 libc 实现。以下所有行为都是一致的:

什么都不做,即让数据保持在原处并返回旧块,可能会重新使用现在未使用的字节进行进一步分配(afaik 这种重复使用并不常见) 将数据复制到新块并将旧块释放回操作系统 将数据复制到新块并保留旧块以供进一步分配

也可以

如果无法分配新块,则返回空指针

在这种情况下,旧数据也将保留在原处,这可能导致内存泄漏,例如,如果realloc() 的返回值覆盖了指向该块的指针的唯一副本。

一个明智的 libc 实现将使用一些启发式方法来确定哪种解决方案最有效。

还请记住,此描述处于实现级别:语义上,realloc() 总是释放对象,只要分配没有失败。

【讨论】:

关于“afaik 这种重用并不常见”,我从未听说过将旧分配留在原地并且不释放尾部以供重用的实际实现。这将是病态的不良行为,尽管是合法的。 是否有定义这种行为的标准,还是真的取决于 libc 实现? @rr-:C 标准以及 POSIX 将其留给实现;我不知道任何定义行为的规范,但它可能记录在 libc 手册中【参考方案4】:

19995 字节似乎不太可能被释放。 更有可能的是 realloc 将 20000 字节块替换为另一个 5 字节块, 即释放了 20000 字节的块,并分配了一个新的 5 字节的块。

【讨论】:

【参考方案5】:

realloc 函数有如下合约:

void* 结果 = realloc(ptr, new_size)

如果结果为 NULL,则 ptr 仍然有效且未更改。 如果结果为非 NULL,则 ptr 现在无效(就像它已被释放一样)并且不得再次使用。 result 现在是指向完全 new_size 字节数据的指针。

幕后发生的确切细节是特定于实现的 - 例如 result 可能等于 ptr (但不能再触及 new_size 之外的额外空间)并且 realloc 可能会调用 free ,或者可能会执行自己的内部操作免费代表。关键是,作为开发人员,如果 realloc 返回非 null,则您不再需要对 ptr 负责,如果 realloc 返回 NULL,您仍然需要对它负责。

【讨论】:

另外,传递new_size = 0与free(ptr)相同,返回NULL。这将是 result == NULL 和 ptr 不再有效的情况。

以上是关于我可以假设调用较小大小的 realloc 会释放其余部分吗? [复制]的主要内容,如果未能解决你的问题,请参考以下文章

如果失败,realloc 会释放前一个缓冲区吗?

关于new操作符如何实现C中的realloc函数

为啥使用 realloc() 时会出现双重释放或损坏错误?

c++内存示例004realloc,释放,对齐分配

我们可以使用 realloc 来释放动态分配的内存吗?

释放分配的内存:realloc() 与 free()