c++ 新建/删除运算符不匹配错误的严重程度如何?

ovfsdjhp  于 2023-01-18  发布在  其他
关注(0)|答案(4)|浏览(151)

我在我们的代码库中发现了典型的new/delete不匹配错误,如下所示:

char *foo = new char[10];

// do something

delete foo; // instead of delete[] foo;

这有多严重?会导致内存泄漏或错误吗?后果是什么?我们有一些内存问题,但这似乎还没有严重到足以解释所有症状(堆损坏等)
编辑:额外问题以澄清
它是否只释放数组的第一个成员?或者
它是否会使系统失去对阵列的跟踪?或者
内存损坏是怎么回事?

tnkciper

tnkciper1#

这是未定义的严重行为(它可能工作,可能崩溃,可能做其他事情)。

j2cgzkjk

j2cgzkjk2#

乍一看,调用delete而不是delete[]应该不是很糟糕:你破坏了第一个对象并引发了内存泄漏。

  • BUT*:然后,delete(或delete[])调用free来释放内存。并且free需要它最初分配的地址来正确地释放内存。或者,事情是,当new返回由malloc分配的最初地址时,new[]返回 * 一个不同的地址 *。

new[]返回的地址上调用free会引发崩溃(它混乱地释放内存)。
请参阅这些非常有启发性的链接以更好地理解:
http://blogs.msdn.com/b/oldnewthing/archive/2004/02/03/66660.aspx#66782
http://web.archive.org/web/20080703153358/http://taossa.com/index.php/2007/01/03/attacking-delete-and-delete-in-c
从这些文章中还可以明显看出,为什么调用delete[]而不是delete也是一个非常糟糕的主意。
因此,要回答:是的,这是一个非常非常严重的错误。2它破坏了内存(只在调用了第一个对象的析构函数之后)。

rekjcdws

rekjcdws3#

对于new[],实现通常在某处存储分配的数组元素的数目,因为它们需要知道delete[]将析构其中的多少。
比较new/deletenew[]/delete的单个对象的分配和解除分配:https://godbolt.org/z/GYTh7f7Y7。可以清楚地看到,后一种情况下的机器码要复杂得多。注意,new[]使用mov QWORD PTR [rax], 1将元素的数量(1)存储到分配内存的开头。delete[]然后使用mov rsi, QWORD PTR [rdi-8]读取该数量,以便能够迭代元素并调用它们的析构函数。
普通的new不存储这个数字,因此,当您将newdelete[]一起使用时,delete[]将读取一些未指定的数字,并将析构函数应用到未预测的内存中。
相反的new[]加上delete的情况也是非常错误的。普通的new表达式通常返回一个指针,该指针精确地指向operator new内部分配的内存块(通常调用malloc)。当该指针传递给delete表达式时,然后在内部将其传递给operator delete释放函数。
但是new[]的情况就不同了,new[]不返回operator new内部获得的指针,而是返回增加了8字节的指针(使用GCC,但我认为x86_64上的Clang和MSVC也是如此)。请参见链接程序集中的lea r12, [rax+8]。在这8个字节中,存储分配的数组元素的数目。因此,如果将delete应用于通过new[]获得的内容,则delete将向operator delete传递一个尚未通过operator new分配的指针,因为它不会从中减去这8个字节。这最终可能会导致一些类似堆损坏的问题。

axzmvihb

axzmvihb4#

正如在其他回答中指出的,new []delete不匹配可能会崩溃。
然而,如果被new 'd或delete' d的类型没有析构函数,那么至少在GCC,MSVC和Clang中,使用new/delete操作符方法的默认实现,将不会有任何结果。这包括基本类型int,char等。
复杂类的new[]delete(或newdelete[])不匹配可能会崩溃。
这是因为如果编译器不需要调用析构函数,它就不会在数据之前插入元素计数。
修改@丹尼尔Langr的godbolt页面,注意new/deletenew[]/delete[]在类没有析构函数时的有限差异:https://godbolt.org/z/hW4f3Ge13(与具有析构函数时相比:https://godbolt.org/z/GYTh7f7Y7)唯一的区别是对operator newoperator new[]的调用以及类似的删除操作。默认情况下,这些函数具有相同的实现。
如果您试图确定堆损坏的原因,这将非常有用。

相关问题