当内存收缩时`realloc()`的行为

问题描述 投票:4回答:2

manrealloc()页面说:

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

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

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

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

任何帮助表示赞赏。

c dynamic-memory-allocation realloc
2个回答
8
投票

首先你可能意味着:

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。一些调试框架(我不记得的那些)在释放内存时放置了一个模式,所以如果你在程序中偶然发现这个模式,这清楚地表明你正在读取一个未分配/未初始化的内存,但它有开销,所以它是默认情况下没做。你可以“重载”内存分配函数来自己做。

无论如何,确保你没有读过新阵列,因为你找不到保证。


5
投票

首先,

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

是错误的,因为你正在使用ptr未初始化(这里定义),并且根据realloc()C11函数描述,章节§7.22.3.5

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

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

但是,考虑到你的情况是这样的

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

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

那说,重新检查报价(强调我的)

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

所以,回答

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

不,我们可以说。成功的realloc()电话后,我们只允许访问ptr + nsize - 1。尝试读取/写入ptr + nsize及其以后是未定义的,因为该内存位置不再属于您的进程,并且内存位置“无效”。

无论如何,你不应该对ptr + nsize - 1以外的内容感到烦恼。

© www.soinside.com 2019 - 2024. All rights reserved.