【问题标题】:Why does a pointer have to be assigned for realloc to work without altering the first value in the memory block?为什么必须为 realloc 分配一个指针才能在不改变内存块中的第一个值的情况下工作?
【发布时间】:2014-08-21 20:09:20
【问题描述】:
int *ptr;
...
realloc(ptr,++count*sizeof(int));
      or
ptr=realloc(ptr,++count*sizeof(int));

我注意到如果我多次使用选项一,第一个内存地址(ptr 指向)的值将变为未定义(尽管内存块中的所有其他值都可以,并且可以通过下标访问ptr)。

但是,我假设所有 realloc 所做的只是缩小或增加内存块的大小,ptr 仍将指向同一个内存块,并且其值都不会改变。那么如果我使用第一个选项,为什么内存块中的第一个地址最终会有一个意外的值,因为ptr 不是仍然指向同一个地址吗?

EDIT:我确实记得给ptr分配内存,只是觉得提不起来。

【问题讨论】:

  • 如果不占用新的内存块,您不能保证会有更多的连续空间可以扩展。
  • @chris:即使有,重用传递给realloc 的指针也是UB,除非realloc 失败。
  • @Deduplicator 即使它指向同一个内存位置?
  • @MattMcNabb: 没有一致的方法可以让你找到,答案是肯定的。 (我也曾经天真地认为不是……)

标签: c memory realloc


【解决方案1】:

realloc 返回的指针可能会或可能不会指向与您传递给它的指针相同的地址。

通常,如果您要扩展内存块,则可能没有足够的空间将其扩展到位。在这种情况下,realloc 分配一个新的内存块,将旧对象的内容复制到新分配的块中,并返回一个指向新块的指针。如果您不将结果分配给指针对象,那么您只是丢失了它(旧块已被释放,而您不知道新块在哪里)。

realloc 甚至可以返回一个指向新分配的内存块的指针,如果它收缩分配的块。从不同的内存区域分配较小的块可能有充分的理由。

您还需要注意realloc 可能会失败。如果是,它返回一个空指针——旧指针仍然有效(只是没有指向你想要的大小的内存块)。 (原则上,即使新大小小于旧大小,realloc 也会失败。)因此,除非您对分配失败的响应是中止程序,或者至少是其中使用指针的部分,否则您应该将结果分配给一个不同的指针对象:

int *ptr = ...;
int *new_ptr;
new_ptr = realloc(ptr, new_size);
if (new_ptr == NULL) {
    /* ptr still points to the old block of memory; you can try something else */
}
else {
    ptr = new_ptr; /* Now you can forget the old pointer value */
}

如果realloc成功,它返回的指针值是有效的,你传给它的指针是无效的。

如果realloc失败,它返回一个空指针,你传递给它的指针仍然有效。

【讨论】:

  • 也许提到,只有在realloc 失败时才允许使用从旧指针派生的任何指针:即使旧指针与新指针按位相同,使用旧指针也是 UB 并可能导致有趣的体验。另外,realloc(not 0, 0) 很有趣。
  • 确实realloc() 可以失败并返回NULL。但是realloc() 可以工作并返回NULL C11dr §J.3.12,由@Deduplicator 提示realloc(not 0, 0)。也许if (new_ptr == NULL && new_size > 0) {
【解决方案2】:

realloc 的实际实现是实现定义的,但通常realloc 会尝试分两步获得更多内存。首先,它尝试查看是否可以扩展已分配的块,而无需将其重新定位到内存中。如果成功,则realloc 不必在内存中移动任何内容,并且旧指针有效。

但是,可能没有空间将分配的块扩展到新大小。例如,您可以想象在当前分配的块之后还有另一个分配的内存块,因此不可能向前扩展边界。在这种情况下,realloc 必须分配一块全新的内存来存储更大的块,旧的指针将不再有效。这就是为什么你应该写

ptr = realloc(ptr, newSize);

而不是

realloc(ptr, newSize);

当然,realloc 可能会移动内存还有其他原因。内存分配器可能对内存不同部分的块有特定的大小要求,或者它可能会尝试通过压缩内存来优化性能。在这两种情况下,realloc 可能会在没有通知的情况下移动内存,因此您可能需要更改ptr 指向的位置。

希望这会有所帮助!

【讨论】:

  • 更正:旧指针无效,即使它与新指针逐位相同。永远不要将它用于任何事情,甚至是比较。
  • @Deduplicator “无效,即使它是位对位相同”的评论起初听起来像你在说if (old_ptr == new_ptr) DoNotUse(old_ptr); 或者你的意思是“即使它无效(数据指向指针)逐位相同”?
  • @chux:旧指针可能根本不能用于任何事情,包括与新指针的比较。我的“逐位”是关于指针,而不是它指向的内容。
  • @Deduplicator 现在我很好奇。 (当然old_ptr = new_ptr; 是可以的)old_ptr 会让printf("%p", (void*) old_ptr) 成为问题吗?也许转换为void*?我开始明白你的意思了。因此,除非指针最初是 void*,否则 printf("%p", (void*) old_ptr) 涉及转换。嗯。
  • @chux:不,转化不相关。任何指向旧对象的指针在realloc(或free)之后都有indeterminate value的原因是编译器有理由假设,在realloc返回一个非零值之后,没有其他指针可以等于它返回的指针,因此没有其他对象可以为指向的对象起别名。因此,即使新指针的数值与旧指针的数值相同,优化编译器也可以并将表达式 old_ptr == new_ptr 编译为常量 0。
猜你喜欢
  • 2019-10-17
  • 2016-11-06
  • 2019-02-24
  • 2014-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-06-20
  • 1970-01-01
  • 2013-03-20
相关资源
最近更新 更多