C++ ReloLc相同指针保证

C++ ReloLc相同指针保证,c++,dynamic-memory-allocation,realloc,C++,Dynamic Memory Allocation,Realloc,使用std::realloc函数: 如果新的大小更小,它是否总是保证将内存块保持在相同的位置,并且只使其变小,或者它有时可以移动整个内存块 提出这个问题的原因是,我们正在编写一个大而硬的代码,当我们试图更改错误的变量时,将所有需要保持不变的变量设置为只读是很有用的,这样可以获得编译器的错误 #include<cstdlib> #include<iostream> using namespace std; int main(){ //From 10,000,000 u

使用std::realloc函数:

如果新的大小更小,它是否总是保证将内存块保持在相同的位置,并且只使其变小,或者它有时可以移动整个内存块

提出这个问题的原因是,我们正在编写一个大而硬的代码,当我们试图更改错误的变量时,将所有需要保持不变的变量设置为只读是很有用的,这样可以获得编译器的错误

#include<cstdlib>
#include<iostream>
using namespace std;

int main(){
  //From 10,000,000 unsigned ints to 10 unsigned ints
  unsigned int * const array=new unsigned int[10000000];
  cout<<array<<endl;
  realloc(array,10*sizeof(unsigned int));
  cout<<array<<endl;
  delete array;
  return 0;
}

realloc没有任何保证。它可能会就地收缩块,也可能会分配一个新块并复制数据。它也可能失败

重要的一点是:realloc仅用于重新分配以前由malloc分配的内存。在上面的代码中,您使用的是new,它没有用于重新分配的等价物

此外,realloc实际上返回新内存块的地址,因此在上面的代码中,您将对此进行泄漏,并引用/释放可能已取消分配的内存。

草案7.20.3.4说:

[4] realloc函数返回指向新 对象,该对象的值可能与指向 旧对象,或者如果新对象无法访问,则为空指针 被分配

你不应该假设它

还有:不要像πάντα已经在评论中写的那样混合使用new和realloc。

否!!如果realloc成功,则旧指针(除非它是空指针)是不确定的

另外,除非另有保证,否则不要混合使用不兼容的内存管理功能

realloc仅具有标准中明确给出的保证:

如果返回值为非0:则新指针指向的大小至少为字节,第一个minoldsize,newsize等于传递的块。 否则,如果size为非0,则传递的块不会发生任何变化。 否则,旧区块可能已被解除分配,或者没有。 寓意:永远不要将0大小传递给realloc,如果realloc失败或传递了null指针,则只使用旧指针进行任何操作,包括与新指针的比较

7.22.3.5 realloc功能 2 realloc函数解除分配ptr指向的旧对象,并返回一个 指向大小由size指定的新对象的指针。新报告的内容 对象应与解除分配前的旧对象相同,以较小者为准 新的和旧的尺寸。新对象中超出旧对象大小的任何字节都将被删除 不确定值。 3如果ptr是空指针,realloc函数的行为与 指定的大小。否则,如果ptr与内存先前返回的指针不匹配 管理功能,或者如果空间已通过调用免费或 realloc函数,行为未定义。如果无法创建新对象的内存 分配后,旧对象不会被解除分配,其值不变

返回 4 realloc函数返回指向新对象的指针,该对象可能具有相同的 值作为指向旧对象的指针,如果无法创建新对象,则为空指针 分配的


尽管我同意其他答案,你不应该依赖它,但在glibc源代码中可以找到一个答案。我假设您正在使用glibc,因为您还没有回答我关于您正在使用哪个C库的问题

编辑:正如其他答案所提到的,在new分配的内存上使用realloc确实是不允许的

未在内部使用mmap的情况下分配内存 如果未使用mmap分配内存块,则调用包含以下代码段的函数:

if ((unsigned long) (oldsize) >= (unsigned long) (nb))
 {
   /* already big enough; split below */
   newp = oldp;
   newsize = oldsize;
 }
这使得指向新内存的指针与指向旧内存的指针相等,并相应地设置大小。请注意下面的拆分注释;旧内存块的大小可以调整为请求的大小,但不会移动

使用mmap在内部分配的内存 如果内存是使用mmap在内部分配的,则有两种方法可以调整内存区域的大小;mremap_chunk或对malloc、memcpy和free的一系列调用。如果mremap_chunk函数可用,则使用该函数代替后一个选项

使用mremap_块重新分配内存 函数包含这段代码

/* No need to remap if the number of pages does not change.  */
if (size + offset == new_size)
    return p;
如果页数没有从旧大小更改为新大小,则无需重新映射,并返回旧指针

使用malloc、memcpy和free重新分配内存 如果mremap_块不可用,则源继续执行以下操作:

/* Note the extra SIZE_SZ overhead. */
if (oldsize - SIZE_SZ >= nb)
    return oldmem;                         /* do nothing */
如果oldsize变量减去区块大小大于或等于新大小,只需返回旧内存即可

那么,我们到了。在所有情况下,glibc都会返回一个指向旧内存的指针,不移动它,但可能会调整它的大小。如果您正在使用glibc,并且能够以某种方式保证您正在使用它的唯一C库是glibc,并且能够保证它在将来某个时候不会改变,那么y

如果请求的大小等于或小于旧大小,您可以依靠realloc不会移动内存块的行为。

您不应该混合使用new和realloc。您不能realloc分配给new的内存。您使用的是哪个C库?realloc来自C图书馆你不能把新的和realloc混合在一起。因此,不要问这个问题,也不要问你的这个问题:,而是写你自己的realloc版本,它尊重新的操作符。这样您就不会有任何问题了,因为您可以精确地控制自制的realloc函数的行为。realloc可能会移动分配的内存块,即使它会收缩,以改进未来的分配。例如,大的分配可能来自一个内存区域,小的分配可能来自另一个内存区域。正如他所说,他想知道它是否安全,而不是安全。如果有一个保证,如果传递的块至少是大字节,realloc总是返回同一个指针,不保存返回的指针是一个有效的选择,因为您知道您只是在缩小块。因此,最后一段与回答他的问题完全是多余的。@Deduplicator:我说的第一件事是realloc没有任何保证。也许你需要提高你的阅读理解能力?好吧,我试着给你换个说法:他提供代码来说明他的问题,考虑到他要求缩小或至少不扩大块,他是否可以假设realloc返回的指针就是他传递的指针。您的答案是:不,realloc不提供这种保证,顺便说一句,您的示例有一个bug,因为realloc不保证块不会移动。你现在看到最后一段充其量是完全多余的了吗?@Deduplicator:没有,因为他使用的函数不正确。指出这一点怎么是多余的?OP没有将0传递给realloc,因此在回答他的问题时,您的大多数答案都是完全多余的。听起来很熟悉吗?好吧,现在你已经够了。我给出了完整的图片,这与谴责他的例子来说明他的问题有些不同。指出内存泄漏的不利影响,错误地使用函数访问释放的内存不是谴责他的例子,而是试图说明为什么他不应该使用他试图使用的方法。抓住点。我最后一次尝试让你看到点,或者像你说的那样,让你抓住点:他的例子说明了他的问题,他是否可以假设新指针与旧指针相同,你看不到对这个问题说“不”和说这个例子所基于的假设不成立是一样的吗?有些人…我最后一次尝试:他说我能做X吗?。我回答说不,你不能做X,因为如果你做了,就会出问题。您回答“不,您不能做Y”。正如您所说,有些人。非常依赖于实现,而且信息不稳定。请注意,即使返回的指针实际上与传递的指针相同,在此之前存储旧指针的所有变量在之后都是不确定的。这不是一个理论上的观点,但在我最近在UB上读到的一篇博文中,用叮当声在野外证明了这一点。我只是很抱歉我失去了链接…这看起来很奇怪-很遗憾你失去了链接,这看起来是一本有趣的书。但是,是的,我同意你的看法;这更多的是关于glibc如何处理这些信息,而不是非常便携或实用的信息。你的答案很好。非常感谢你。我不太明白这句话:编辑:在new分配的内存上使用realloc确实是不允许的,正如其他答案所提到的。您正在要求其他用户编辑他们编写的内容?因为可以将realloc与new和delete一起使用?Edit表示我稍后编辑了文章以包含该信息;我重申,不允许将realloc与new和delete一起使用。也就是说,如果您想深入了解g++的内部结构,您可能会发现将new[]和realloc这样混合使用的方式与您前面提到的工作方式相同。也就是说,这并不能保证,但可能它确实发生在一个或多个特定的实现上,这仅仅是因为为POD类型实现新[]的明显方式。
/* Note the extra SIZE_SZ overhead. */
if (oldsize - SIZE_SZ >= nb)
    return oldmem;                         /* do nothing */