【问题标题】:Behavior of `realloc()` when the memory is shrunk缩小内存时`realloc()`的行为
【发布时间】:2019-08-04 04:00:55
【问题描述】:

realloc()man 页面说:

realloc() 函数将ptr 指向的内存块大小更改为size 字节。内容将在从区域开始到新旧大小的最小值的范围内保持不变。如果新大小大于旧大小,则不会初始化添加的内存。

但是,手册页没有说明如果新大小小于旧大小会发生什么。例如,如果我有以下代码:

ptr = realloc(ptr, nsize); // Where nsize < the original size and ptr is of type void **

如果原始大小为size,这是否意味着ptr + nsize + 1 仍包含已分配的条目?

感谢任何帮助。

【问题讨论】:

  • 在从区域开始到新旧尺寸的最小值的范围内内容将保持不变 - 它是一半你的引文。
  • @Jabberwocky 不一定只是缩写。它仍然可以在不同的地方重新分配。
  • 删除帐号的心理提示 :)
  • 除非ptr 来自不同的范围,否则你们都同意void **ptr = realloc(ptr,size) 是一个大问题,对吧?
  • ptr 参数可以是什么其他范围? 错误并不差,因为传递的参数必须是NULL 或先前分配的指针。

标签: c dynamic-memory-allocation realloc


【解决方案1】:

首先你可能是指:

void **ptr = malloc(nsize*2);

然后

ptr = realloc(ptr, nsize);

或安全的方式:

void **ptr2 = realloc(ptr, nsize);
if (ptr2 != NULL)
{
   ptr = ptr2;
} // else failure

因为使用realloc(ptr,nsize)设置ptr 的值是未定义的行为并且可能会崩溃。

现在,系统减少了Can I assume that calling realloc with a smaller size will free the remainder?中所述的内存大小

现在你的问题是:

如果原始大小是size,这是否意味着ptr + nsize + 1 仍然包含已分配的条目?

您对此没有任何保证。这已经是来自 ptr + nsize 的未定义行为(感谢 Sourav)。

为什么?此区域不再属于您的程序。

您可能会在读取新的较小数组时遇到错误,如果旧数据在那里,这将产生有效的结果,这很可能是真的,但是:

  • 系统可以保留相同的内存位置,但会立即将此块重用于其他数据
  • 系统可以将新数据移动到另一个内存位置(所以旧的ptr 与新的ptr 不同,因此返回值会被一些人忽略并且它“有效”直到它崩溃了),在这种情况下,背后是完全不相关的数据。

如果上述两种情况都没有发生,则很可能数据没有变化。 realloc 不会将一些不应该使用的内存设置为 0。一些调试框架(我不记得了)在释放内存时放置了一个模式,所以如果你在程序中偶然发现这个模式,这清楚地表明你正在读取一个未分配/未初始化的内存,但它有开销,所以它是默认情况下未完成。您也可以自己“重载”内存分配函数。

无论如何,请确保您没有阅读过新数组,因为无法保证您会找到什么。

【讨论】:

  • you have no guarantee of that...umm,为什么? nsize 是新尺寸吧?
  • 加一无关紧要,是的。但是如果nsize 是新的大小,那么过去的所有数据都是不可靠的。我就是这个意思。
  • 感谢您的回复(是的,我并不是要在ptr 前面写void **,因为那会初始化它)。那么,在重新分配内存之前,我应该循环获取ptr + nsize + 1 中的数据并释放它以防止内存泄漏吗?
  • 免费吗?没有realloc 已经这样做了。您可以将其设置为 0 或 0xDEADBEEF 或任何其他值,以防万一您之后偶然发现。
  • 这是一个小问题,安全编程当然需要使用临时存储realloc返回值,但很难看出realloc如果被要求减小大小会失败分配,因为如果返回错误,则需要保留先前的分配。因此,即使realloc 想要将存储复制到新区域并且找不到内存来执行此操作,它仍然可以选择仅返回原始分配。
【解决方案2】:

首先,

 void **ptr = realloc(ptr, nsize); 

错了,因为您使用的是未初始化的 ptr(在此处定义),并且根据 C11 中的 realloc() 函数描述,第 7.22.3.5 章

如果 ptr空指针,则 realloc 函数的行为类似于 malloc 函数 规定的大小。否则,如果ptr 不匹配内存早先返回的指针 管理函数,或者如果空间已通过调用 free 或 realloc 函数,行为未定义。 [...]

因此,您的代码会调用未定义的行为,因为您正在传递一个包含不确定值的指针。

但是,考虑到您的情况类似于

void **ptr = malloc(size);
assert (ptr);
ptr = realloc(ptr, nsize);

这是一个非常糟糕的用法,如果realloc 失败(它不会改变原始内存并返回NULL),你最终也会丢失实际的指针。使用中间变量来存储 validate 返回的指针,然后根据需要将其赋回原始变量。

也就是说,重新检查报价(强调我的

realloc() 函数将ptr 指向的内存块的大小更改为大小字节。 从区域开始到新旧大小的最小值的范围内内容将保持不变。如果新大小大于旧大小,则不会初始化添加的内存.

所以,回答

如果原始大小是size,这是否意味着ptr + nsize + 1 仍然包含已分配的条目?

不,我们不能说。 realloc()调用成功后,我们最多只能访问ptr + nsize - 1。尝试读/写ptr + nsize 及以后是未定义的,因为该内存位置不再属于您的进程并且该内存位置是“无效的”。

反正ptr + nsize - 1 以外的内容,您应该不必担心。

【讨论】:

    猜你喜欢
    • 2016-07-04
    • 2014-04-04
    • 2014-07-02
    • 2021-12-23
    • 1970-01-01
    • 2011-11-20
    • 2013-03-09
    • 2012-11-01
    • 2017-06-15
    相关资源
    最近更新 更多