新建/删除运算符不匹配错误的严重程度
How serious is the new/delete operator mismatch error?
我在我们的代码库中发现了典型的新/删除不匹配错误,如下所示:
char *foo = new char[10];
// do something
delete foo; // instead of delete[] foo;
这到底有多严重?它是否会导致内存泄漏或错误?后果是什么。我们有一些内存问题,但这似乎不足以解释我们的所有症状(堆损坏等)
编辑:为清晰起见,需要额外提问
它只是释放数组的第一个成员吗?或
它是否会使系统失去对阵列的跟踪?或
内存损坏是怎么回事?
它是未定义的严重行为(它可能工作,它可能崩溃,它可能做其他事情)。
乍一看,调用delete
而不是delete[]
应该不会很糟糕:您破坏了第一个对象,并引发了一些内存泄漏。
但是:然后,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[]
而不是删除也是一个非常糟糕的主意。
所以,要回答:是的,这是一个非常非常严重的错误。它破坏内存(仅在调用第一个对象的析构函数之后)。
情况非常严重。对于new[]
,实现通常将分配的数组元素的数量存储在某个地方,因为它们需要知道delete[]
将破坏其中的多少。
将单个对象的分配和释放与new
/delete
和new[]
/delete
进行比较:https://godbolt.org/z/GYTh7f7Y7.您可以清楚地看到,后一种情况下的机器代码要复杂得多。注意,new[]
将元素的数目(1)存储到具有mov QWORD PTR [rax], 1
的所分配存储器的开头。delete[]
然后用mov rsi, QWORD PTR [rdi-8]
读取这个数字,以便能够迭代元素并调用它们的析构函数。
普通的new
不存储这个数字,因此,当您将new
与delete[]
一起使用时,delete[]
将读取一些未指定的数字,并对未预测的内存应用析构函数。这可能会造成严重的漏洞问题。
相反的new[]
加delete
的情况也是非常错误的。普通的new
表达式通常返回一个指针,该指针精确地指向operator new
(通常调用malloc
)内部分配的内存块。该指针在传递到delete
表达式时,会按原样在内部传递给operator delete
释放函数。
但CCD_ 32的情况并非如此。即,new[]
不返回由operator new
内部获得的指针。相反,它返回增加了8个字节的指针(使用GCC,但我认为Clang和x86_64上的MSVC也是如此)。请参见链接部件中的lea r12, [rax+8]
。在这8个字节中,存储分配的数组元素的数量。因此,如果将delete
应用于使用new[]
获得的内容,delete
将向operator delete
传递一个未使用operator new
分配的指针,因为它不会从中减去这8个字节。这最终可能会导致类似堆损坏的情况。
正如其他答案中所指出的,new []
与delete
不匹配可能会崩溃。
但是,如果new'd或delete'd类型没有析构函数,那么至少对于GCC、MSVC和Clang,对于运算符new/delete方法的默认实现,不会有任何后果。这包括int、char等基本类型。
复杂类的new[]
与delete
(或new
与delete[]
)不匹配可能会崩溃。
这是因为如果编译器不需要调用析构函数,它就不会在数据之前插入元素计数。
修改@Daniel Langr的godbolt页面时,如果类没有析构函数,请注意new/delete
和new[]/delete[]
之间的差异有限:https://godbolt.org/z/hW4f3Ge13(与具有析构函数时相比:https://godbolt.org/z/GYTh7f7Y7)唯一的区别是对operator new
和operator new[]
的调用以及对delete的类似调用。默认情况下,这些函数具有相同的实现。
如果您试图确定堆损坏的原因,这将非常有用。
- 警告处理为错误这里有什么问题
- "error: no matching function for call to"构造函数错误
- boost::进程间消息队列引发错误
- C++,OpenCV,尝试显示图像时"OpenCV(4.3.0) Error: Assertion failed (size.width>0 && size.height>0)"此错误
- 有关插入适配器的错误。[错误]请求从 'back_insert_iterator<vector<>>' 类型转换为非标量类型
- QT在错误的班级中寻找空位
- vector.resize()中的分配错误
- 代码在main()中运行,但在函数中出现错误
- 释放错误后堆使用
- (C++)分析树以计算返回错误值的简单算术表达式
- Project Euler问题4的错误解决方案
- 我的字符计数代码计算错误.为什么
- 从"int*"强制转换为"unsigned int"会丢失精度错误
- 尝试导入pybind-opencv模块时出现libgtk错误
- CMake项目Boost库错误:Boost/config/compiler/gcc.hpp:165:10:致命错误:cs
- 在某些循环内使用vector.push_back时出现分段错误
- MSVC多行宏编译器错误
- 静态数据成员的问题-修复链接错误会导致编译器错误
- 为什么在运行时没有向我们提供有关分段错误的更多信息?
- 新建/删除运算符不匹配错误的严重程度