重新分配用new分配的内存是否安全?

时间:2015-11-14 08:25:03

标签: c++ memory-management realloc

根据here编写的内容,new免费商店中分配,而malloc使用,这两个术语通常意味着同样的事情。

根据here编写的内容,realloc可能会将内存块移动到新位置。如果free store和heap是两个不同的内存空间,那么它是否意味着任何问题呢?

具体来说,我想知道使用是否安全

int* data = new int[3];
// ...
int* mydata = (int*)realloc(data,6*sizeof(int));

如果没有,是否有任何其他方式可以安全地分配realloc new内存?我可以分配新区域和memcpy内容,但据我所知,realloc可能会使用相同的区域。

9 个答案:

答案 0 :(得分:46)

您只能realloc通过malloc(或家人,如calloc)分配的内容。

这是因为跟踪内存空闲和使用区域的底层数据结构可能完全不同。

可能是,但绝不保证C ++ new和C malloc使用相同的底层分配器,在这种情况下realloc可以同时适用于这两者。但正式的是在UB-land。而在实践中,它只是不必要的风险。

C ++不提供与realloc对应的功能。

最接近的是std::vector等容器(内部缓冲区)的自动重新分配。

C ++容器的设计方式不允许使用realloc

而不是提供的代码

int* data = new int[3];
//...
int* mydata = (int*)realloc(data,6*sizeof(int));

......这样做:

vector<int> data( 3 );
//...
data.resize( 6 );

但是,如果您绝对需要realloc的一般效率,并且如果您必须接受new原始分配,那么您对效率的唯一追索是使用特定于编译器的方法,知识使用此编译器realloc是安全的。

否则,如果您绝对需要realloc的一般效率,但不是被迫接受new,那么您可以使用mallocrealloc。使用智能指针可以让您获得与C ++容器相同的安全性。

答案 1 :(得分:16)

C ++添加到realloc的唯一可能相关限制是C ++的malloc / calloc / realloc不得以::operator new的形式实现,其free不得以::operator delete的形式实现(根据C ++ 14 [c.malloc] p3-4)。

这意味着您正在寻找的保证在C ++中不存在。但是,这也意味着您可以::operator new实施malloc。如果你这样做,那么从理论上讲,::operator new的结果可以传递给realloc

在实践中,您应该关注new的结果与::operator new的结果不匹配的可能性。 C ++编译器可以是例如合并多个new表达式以使用单个::operator new调用。这是编译器在标准不允许的情况下已经做过的事情,IIRC,标准现在允许它(根据C ++ 14 [expr.new] p10)。这意味着即使你走这条路,你仍然无法保证将new指针传递给realloc做任何有意义的事情,即使它不再是未定义的行为。

答案 2 :(得分:8)

一般来说,不要这样做。如果您正在使用带有非平凡初始化的用户定义类型,则在重新分配 - 复制释放的情况下,{{1}将不会调用对象的析构函数 }}。复制时,副本构造函数也不会被调用。由于对象生存期的使用不正确,这可能导致未定义的行为(请参阅 C ++标准§3.8对象生存期,[basic.life] )。

  

1对象的生命周期是对象的运行时属性。如果一个对象属于类或聚合类型,并且它或其成员之一由除了普通默认构造函数之外的构造函数初始化,则称该对象具有非平凡的初始化。 [注意:通过简单的复制/移动构造函数进行初始化是非平凡的初始化。 - 后注]

     

T类对象的生命周期始于:

     

- 获得具有类型T的正确对齐和大小的存储,并且

     

- 如果对象具有非平凡的初始化,则其初始化完成。

     

类型T的对象的生命周期在以下时间结束:

     

- 如果T是具有非平凡析构函数(12.4)的类类型,则析构函数调用开始,或

     

- 重用或释放对象占用的存储空间。

后来(强调我的):

  

3本国际标准中归属于对象的属性仅适用于给定对象仅在其生命周期

所以,你真的不想在其生命周期内使用

答案 3 :(得分:5)

这不安全,而且不优雅。

有可能覆盖new / delete以支持重新分配,但您也可以考虑使用容器。

答案 4 :(得分:4)

是 - 如果"实际上首先调用了new(例如,这就是VC ++ malloc的工作方式)。

否则。请注意,一旦您决定重新分配内存(因为new称为new),您的代码将特定于编译器,并且不再在编译器之间移植。

(我知道这个答案可能会让很多开发人员感到不安,但我的回答取决于真实的事实,而不仅仅是惯用语。)

答案 5 :(得分:4)

这不安全。首先,您传递给realloc的指针必须是从mallocreallochttp://en.cppreference.com/w/cpp/memory/c/realloc获得的。

其次new int [3]的结果不必与分配函数的结果相同 - 可以分配额外的空间来存储元素的数量。

(对于比int更复杂的类型,realloc不安全,因为它不会调用复制或移动构造函数。)

答案 6 :(得分:3)

你可能(不是在所有情况下),但你不应该。如果您需要调整数据表的大小,则应使用std::vector代替。

有关如何使用它的详细信息列在另一个SO question

答案 7 :(得分:3)

一般来说,没有。

为确保安全,必须坚持下去:

  1. 按位复制类型并放弃源必须是安全的。
  2. 析构函数必须是微不足道的,否则你必须就地破坏你想要解除分配的元素。
  3. 构造函数是微不足道的,或者您必须就地构造新元素。
  4. 琐碎的类型满足上述要求。

    另外:

    1. new[] - 函数必须将请求传递给malloc而不做任何更改,也不要进行任何记账。您可以通过替换global new []和delete []或相应类中的那些来强制执行此操作。
    2. 编译器不得要求更多内存以保存分配的元素数量或其他任何内容 没有办法强制这样做,尽管编译器不应该保存这样的信息,如果类型具有一个简单的析构函数作为实施质量的问题。

答案 8 :(得分:-1)

这些功能主要用于C。

memset将内存块中的字节设置为特定值。

malloc分配一块内存。

calloc,与malloc相同。唯一的区别是它将字节初始化为零。

在C ++中,分配内存的首选方法是使用new。

C:int intArray =(int *)malloc(10 * sizeof(int)); C ++:int intArray = new int [10];

C:int intArray =(int *)calloc(10 * sizeof(int)); C ++:int intArray = new int10;