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

Posted

技术标签:

【中文标题】调用 free() 或 delete 而不是 delete[] 有啥危险吗? [复制]【英文标题】:Is there any danger in calling free() or delete instead of delete[]? [duplicate]调用 free() 或 delete 而不是 delete[] 有什么危险吗? [复制] 【发布时间】:2010-12-09 09:09:26 【问题描述】:

可能重复:( 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[] 怎么推导出Objects 的数量超过第一个,这不是说它必须知道分配的内存区域的大小吗?如果出于性能原因分配的内存区域有一些悬垂怎么办?例如,可以假设并非所有分配器都提供单个字节的粒度。那么任何特定的分配都可能超过每个元素所需的大小一个或更多的元素。

对于基本类型,如charint,有没有区别:

int *p = new int[n];
delete p;
delete[] p;
free p;

除了通过delete->free释放机制的各个调用所采用的路由?

【问题讨论】:

我不相信这是重复的,我在问一些非常具体的不同问题,对汇编内涵不感兴趣 不,它是重复的。您在问 exact 相同的问题“我可以用 delete 代替 delete[]”。答案与之前所有被问到的线程中的相同:“不,你不是。这是未定义的行为”。 如果你想问 other 问题(比如“delete[] 如何知道要删除多少对象”,然后为此创建一个新问题,给它自己标题。以便其他想问同样问题的人能够找到它。 【参考方案1】:

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

您只是在查看您在开发人员方面看到的内容。 您没有考虑的是标准库如何进行内存管理。

第一个区别是 new 和 malloc 从内存中的两个不同区域分配内存(来自 FreeStore 的 New 和来自 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 和 C++ 应用程序之间的内存分配差异,您会发现它们非常不同。因此,使用完全不同的内存管理技术来优化应用程序类型并不是不合理的。这是在 C++ 中更喜欢 new 而不是 malloc() 的另一个原因,因为它可能会更有效(尽管更重要的原因始终是降低复杂性 (IMO))。

【讨论】:

【参考方案2】:

对于原始类型,比如char、int,有什么区别:

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

【讨论】:

不,m“实现定义”是 ISO 在编译器供应商必须记录他们的选择时使用的术语。在这种情况下,没有这样的义务。无需警告就允许使用鼻恶魔。 这里已经讨论过了:***.com/questions/1553382/… 我不知道 ISO 使用了这样的术语。固定。【参考方案3】:

这是未定义的行为(很可能会立即损坏堆或使程序崩溃),您永远不应该这样做。仅释放具有与用于分配该内存的原语相对应的原语的内存。

违反此规则可能会碰巧导致正常运行,但一旦更改任何内容(编译器、运行时、编译器设置),程序就会中断。您永远不应该依赖这种正常的功能并期望它。

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

【讨论】:

【参考方案4】:

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

没有。无论您执行哪个编译器, delete 都只会释放第一个元素。在某些情况下它可能会起作用,但这是偶然的。

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

取决于内存如何标记为空闲。再次依赖于实现。

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

没有。试试这个:

#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;

delete[] 怎么推导出数量 超越第一个的对象,不会 这意味着它必须知道 分配的内存区域?

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

如果内存区域是 分配了一些悬垂的 性能原因?例如一个 可以假设不是所有的分配器 将提供一个粒度 单字节。那么任何特定的 分配可能超过要求 每个元素的大小为一个整体 元素或更多元素。

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

对于原始类型,比如char、int,...之间有什么区别吗?

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

【讨论】:

我认为你的意思是 new 和 malloc 可能使用不同的分配器? 可能会有额外的“悬垂”。对齐与块的起始地址有关。出于性能原因,分配器可能会使用更多内存。例如,有些分配器只分配大小是 2 的幂的块。如果你要求 33 个字节,你会得到一个大小为 64 的块。这使得管理已分配和空闲块的列表更容易/更快,在增加内存使用的代价。分配器甚至可以仅从起始地址知道分配的大小,从而无需存储其他信息。 @Anacrolix:您可能是对的,尽管通常分配器是操作系统。我的回答主要基于 K&R 中的分配器示例...我假设每个分配器从操作系统获取不同的内存块。【参考方案5】:

是的,确实存在实际危险。抛开实现细节不谈,请记住operator new/operator deleteoperator new[]/operator delete[] 函数可以完全独立地替换。因此,明智的做法是将new/deletenew[]/delete[]malloc/free 等视为不同的、完全独立的内存分配方法,它们完全没有共同点。

【讨论】:

即使它们可以独立更换,通常也建议要么全部更换(包括放置新),要么根本不更换。 是的,也许。但是即使它们都被替换了,它们仍然可以使用不同的内存管理原则从不同的内存池中分配内存(出于显而易见的原因,例如'[]'函数中的块大小即使对于相同的对象类型也是可变的,而在非' []' 函数,它们不是),因此使任何交叉使用完全不可行。【参考方案6】:

Raymond Chen(Microsoft 开发人员)有一篇深入的文章,涵盖缩放器与矢量删除,并提供了一些差异的背景。见:

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

【讨论】:

【参考方案7】:

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

在上述情况下,看到 s 的所有元素都是连续分配的,是否应该只删除数组的一部分?

是的。

delete[] 怎么能推导出第一个以外的对象个数,这不是说它必须知道分配的内存区域的大小吗?

编译器需要知道。见FAQ 16.11

因为编译器存储了这些信息。

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

【讨论】:

如果编译器存储了 delete[] 所需的信息,为什么它不能足够聪明地使用普通删除? “因为编译器存储了这些信息。”这是一堆废话。 operator new 是否使用数组分配的元素计数保留所有分配的重复项?不,这对我来说似乎不太可能而且效率很低。 @Anacrolix:我指的是编译器为分配/解除分配管理的簿记信息。我不确定你所说的重复是什么意思。 VS 用于在数组开始之前保留数组元素的数量。你能提供替代实现吗? @vanja:对于简单的新建/删除,需要的簿记信息要少得多。另一方面,对于数组,编译器必须分配比您将使用的多一点。 @vanja:如果您将 vanilla delete 用于指向单个对象的指针和指向对象数组的指针,编译器将不会知道另一个。常见问题 16.11【参考方案8】:

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

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

【讨论】:

【参考方案9】:

是的,这很危险!

别这样!

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

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

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

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

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

【讨论】:

这是众所周知的。我问的是具体情况。 ""free() 释放 ptr 指向的内存空间,该空间必须由之前对 malloc()、calloc() 或 realloc() 的调用返回。否则,或者如果之前已经调用过 free(ptr),则会发生未定义的行为。如果 ptr 为 NULL,则不执行任何操作。 """。注意关于 null 的子句。来自linux.die.net/man/3/free,但我手头没有正式的 C 规范。 没有具体案例。它……是……不……是允许的。【参考方案10】:

这是未定义的行为。因此,答案是:是的,可能存在危险。并且无法准确预测什么会引发问题。哪怕一次有效,还会再有效吗?这取决于类型吗?元素数量?

【讨论】:

以上是关于调用 free() 或 delete 而不是 delete[] 有啥危险吗? [复制]的主要内容,如果未能解决你的问题,请参考以下文章

使用CoTaskMemAlloc时,我应该总是调用CoTaskMemFree吗?

malloc/free与new/delete的区别与联系

C语言中free函数的用法

c++面试题1

c的free和c++的delete的区别

`main' 中的错误:free():无效指针(尽管没有使用 free 或 delete)