调用free()或delete而不是delete []有危险吗?

时间:2009-10-23 08:14:54

标签: c++ memory-management free new-operator

  

可能重复:
  ( POD )freeing memory : is delete[] equal to delete ?

delete是否释放了数组中第一个元素之外的元素?

char *s = new char[n];
delete s;

在上述情况下是否重要,因为s的所有元素都是连续分配的,并且delete不应该只有一部分数组?

对于更复杂的类型,delete会调用超出第一个对象的析构函数吗?

Object *p = new Object[n];
delete p;

delete[]如何推断出Object之外的char数量,这是否意味着它必须知道分配的内存区域的大小?如果出于性能原因为内存区域分配了一些突出怎么办?例如,可以假设并非所有分配器都提供单个字节的粒度。然后,任何特定的分配都可能超过每个元素所需的大小或整个元素。

对于基本类型,例如intint *p = new int[n]; delete p; delete[] p; free p; ,它们之间有什么区别:

delete

除了通过free - > {{1}}解除分配机制进行的各个来电所采用的路线?

10 个答案:

答案 0 :(得分:15)

这是未定义的行为(很可能会破坏堆或立即崩溃程序),你永远不应该这样做。只有具有与用于分配该内存的原语对应的原语的空闲内存。

违反此规则可能会导致巧合,但一旦发生任何变化,程序就会崩溃 - 编译器,运行时,编译器设置。你永远不应该依赖这样的正常运作并期待它。

delete[]使用特定于编译器的服务数据来确定元素的数量。通常在调用new[]时分配更大的块,数字存储在开头,调用者被赋予存储数字后面的地址。无论如何delete[]依赖于new[]分配的块,而不是其他任何东西。如果您将new[]以外的任何内容与delete[]配对,反之亦然,则会遇到未定义的行为。

答案 1 :(得分:10)

阅读常见问题解答:16.3 Can I free() pointers allocated with new? Can I delete pointers allocated with malloc()?

  

在上述情况下是否重要,因为s的所有元素都是连续分配的,并且不应该只删除数组的一部分?

是的。

  

如何删除[]推断超出第一个的对象数量,这是不是意味着它必须知道分配的内存区域的大小?

编译器需要知道。见FAQ 16.11

  

因为编译器存储了该信息。

我的意思是编译器需要不同的delete来生成适当的簿记代码。我希望现在很清楚。

答案 2 :(得分:5)

是的,这很危险!

不要这样做!

这将导致程序崩溃甚至更糟糕的行为!

对于使用new分配的对象,您必须使用delete;

对于使用new []分配的对象,您必须使用delete [];

对于使用malloc()calloc()分配的对象,您必须使用free();

还要注意,对于所有这些情况,第二次删除/释放已经删除/释放的指针是非法的。 free也可能不会被调用null。使用NULL调用delete/delete[]是合法的。

答案 3 :(得分:3)

是的,这确实存在实际危险。除了实现细节之外,请记住operator new/operator deleteoperator new[]/operator delete[]函数可以完全独立地替换。出于这个原因,明智的做法是将new/deletenew[]/delete[]malloc/free等视为不同的,完全独立的内存分配方法,它们完全没有任何共同点。

答案 4 :(得分:2)

  

delete是否释放元素   超出数组中的第一个?

没有。 delete将仅解除分配第一个元素,无论您使用哪个编译器。它可能在某些情况下有效,但这是偶然的。

  

在上述情况下是否重要,因为s的所有元素都已分配   连续,并且不应该只删除数组的一部分吗?

取决于内存如何自由市场化。再次依赖于实现。

  

对于更复杂的类型,是否会删除调用第一个之外的对象的析构函数?

没有。试试这个:

#include <cstdio>

class DelTest {
    static int next;
    int i;
public:
    DelTest() : i(next++) { printf("Allocated %d\n", i); }
    ~DelTest(){ printf("Deleted %d\n", i); }
};

int DelTest::next = 0;

int main(){
    DelTest *p = new DelTest[5];
    delete p;
    return 0;
}
  

如何删除[]推断出的数量   超出第一个的对象,不会   这意味着它必须知道的大小   分配了内存区域?

是的,尺寸存放在某个地方。存储的位置取决于实现。例如,分配器可以将大小存储在分配地址之前的标头中。

  

如果内存区域是什么   分配了一些悬念   表现原因?例如一个   可以假设并非所有分配器   将提供a的粒度   单字节。然后任何特别的   分配可能超过要求   每个元素的大小整体   元素或更多。

正是由于这个原因,返回的地址与字边界对齐。使用sizeof运算符可以看到“悬垂”,也适用于堆栈上的对象。

  

对于原始类型,例如char,int,...?

之间有什么区别

是。 malloc和new可以使用单独的内存块。即使不是这种情况,最好不要假设它们是相同的。

答案 5 :(得分:2)

Raymond Chen(微软开发人员)有一篇关于缩放器与矢量删除的深入文章,并给出了差异的一些背景知识。参见:

http://blogs.msdn.com/oldnewthing/archive/2004/02/03/66660.aspx

答案 6 :(得分:1)

  

对于基本类型,例如char,int,它们之间有什么区别:

我会说你会得到未定义的行为。所以你不应该指望稳定的行为。你应该总是使用new / delete,new [] / delete []和malloc / free对。

答案 7 :(得分:1)

这是未定义的行为。因此,anser是:是的,可能存在危险。并且无法准确预测会引发问题的原因。即使它有效一次,它会再次起作用吗?它取决于类型吗?元素数量?

答案 8 :(得分:0)

虽然看起来可能会以某种逻辑方式混合new []和free或delete而不是delete [],但这是假设编译器相当简单,即它总是使用malloc( )为new []实现内存分配。

问题在于,如果您的编译器具有足够智能的优化器,则可能会看到没有与您创建的对象的新[]对应的“delete []”。因此,它可以假设它可以从任何地方获取内存,包括堆栈,以节省为new []调用真实malloc()的成本。然后当你试图调用free()或者错误的删除时,它很可能会出现故障。

答案 9 :(得分:0)

第1步阅读:what-is-the-difference-between-new-delete-and-malloc-free

您只看到您在开发者方面看到的内容 你没有考虑的是std lib如何进行内存管理。

第一个区别是new和malloc从内存中的两个不同区域分配memroy(来自FreeStore的新内容和来自Heap的malloc(不要关注它们基本上都是堆的名称,那些只是标准中的正式名称) ))。如果你从一个分配并解除分配到另一个,你将搞乱用于管理内存的数据结构(没有保证他们将使用相同的结构进行内存管理)。

当你像这样分配一个块时:

int*   x= new int; // 0x32

记忆可能看起来像这样:它可能不会因为我没有想到那么难以做到这一点。

Memory   Value      Comment
0x08     0x40       // Chunk Size  
0x16     0x10000008 // Free list for Chunk size 40
0x24     0x08       // Block Size
0x32     ??         // Address returned by New.
0x40     0x08       // Pointer back to head block.
0x48     0x0x32     // Link to next item in a chain of somthing.

关键是分配的块中有更多信息,而不仅仅是为处理内存管理而分配的int。

标准没有说明这是如何做的,因为他们不希望在编译器/库制造商能够为架构实现最有效的内存管理方法方面做到这一点。

考虑到这一点,您希望制造商能够区分数组分配/解除分配与正常分配/解除分配,以便能够使两种类型的存储尽可能有效。因此,您不能在内部混合和匹配,他们可能会使用不同的数据结构。

如果您实际分析了C和C ++应用程序之间的内存分配差异,您会发现它们非常不同。因此,使用完全不同的内存管理技术来优化应用程序类型并不是不可思议的。这是在C ++中更喜欢新的malloc()的另一个原因,因为它可能会更有效(更重要的原因,但总是降低复杂性(IMO))。