在编译时检测delete []与delete的滥用


19

我想知道是否有可能delete在编译时检测到以下注释的错误?特别是,我想了解g ++编译器。

ClassTypeA *abc_ptr = new ClassTypeA[100];  
abc_ptr[10].data_ = 1;  
delete abc_ptr; // error, should be delete []  

7
无论如何,您都不应该手动调用delete。
马丁·约克

9
@LokiAstari您实际上认为该评论有帮助吗?
詹姆斯

5
@詹姆斯:是的。关键是“手动”。
马丁·约克

有时要遵守此要求将涉及重写许多旧代码
Nick Keighley

使用std::unique_ptr<ClassTypeA[]>,然后您不需要。
user253751'17

Answers:


6

通常,编译器无法检测到此类错误。示例:假设某个类的构造函数使用分配了一些数据成员new TypeName[],但析构函数错误地使用delete而不是delete[]。如果构造函数和析构函数是在单独的编译单元中定义的,则编译器如何在编译定义析构函数的文件时知道其用法与定义构造函数的单独编译文件中的用法不一致?

关于GNU编译器,事实并非如此。如上所述,在一般情况下不能这样做。编译器不必检测此类不匹配的新/删除错误,因为这是未定义的行为。UB是编译器供应商的“摆脱监狱”卡。

诸如valgrind之类的工具可以并且确实能够检测到这些新的/删除的不匹配,但是可以在运行时检测到。可能会有一个静态分析工具查看所有最终将被编译以形成可执行文件的源文件,但是我没有任何此类静态分析工具能够检测到此类错误。


我使用了一个名为Parasoft的静态分析工具,该工具肯定对此特定情况具有规则。它确实可以在特定项目中的所有文件上运行(如果配置正确)。话虽这么说,但我不确定它如何处理诸如Pete Kirkham对Kilian Foth的回答的评论。
迅猛龙

28

您可以将适当的RAII类用于delete。这是唯一安全的方法,该错误只是您delete自己遇到的很多错误之一。

始终使用类来管理动态生命周期资源,类型系统将强制执行正确的资源销毁。

编辑:“如果您正在审核代码并且无法更改它,该怎么办?” 你他妈的。


18
-1,因为这实际上不能回答问题。
梅森惠勒

2
检测不匹配的唯一方法是使用类型系统,这涉及使用RAII类。
DeadMG

9
...这更没有意义。RAII类(一种运行时机制)的使用与编译器在编译时了解的静态类型系统信息有什么关系?
梅森惠勒

6
@MasonWheeler参见boost :: shared_ptr和boost :: shared_array作为示例。销毁shared_ptr会删除对象,销毁shared_array delete [] s数组。您不能将shared_array分配给shared_ptr,因此-只要您不首先构建带有数组的shared_ptr,类型系统就可以防止使用错误的删除。
Pete Kirkham

4
通常,这样的回答比帮助更令人讨厌。但是,在这种情况下,实际上是这样。他正在寻找常见错误的编译器实施,并且使用RAII可以正确地防止这种错误,从而准确地提供所需的信息。+1
riwalk

10

这个特别的错误-是的。这种类型的错误一般:不幸的是,没有!那将涉及在不实际执行的情况下预测执行流程,而这对于任意程序都是不可能的。(这就是为什么大多数编译器甚至不尝试检测像您的示例这样的简单情况的原因。)

因此,DeadMG的答案是合适的答案:不要试图通过注意力使它正确-人类的注意力是容易犯错的。使用语言提供的方法,并让计算机注意。


这如何要求预测执行流程?在我看来,这似乎是纯静态的编译时知识。编译器的类型系统知道什么是数组,什么不是。
梅森惠勒

即使有演员阵容?抱歉,如果我记错了,我将删除答案。
Kilian Foth,

12
@MasonWheeler是abc_ptr的静态类型,ClassTypeA*因此您可以在new和delete之间插入一行。if ( rand() % 2 == 1 ) abc_ptr = new ClassTypeA;静态类型系统中的任何内容都不会显示是abc_ptr指向数组还是指向动态对象,还是指向另一对象或数组的一部分。
Pete Kirkham

...啊对。我已经习惯于使用具有真正数组类型的语言,以至于我忘了它在C-land中是如何搞砸的。:(
梅森惠勒

1
@Pete Kirkham,@ Mason Wheeler:但是,运行时仍应查看在指向的地址处存储了多少个对象abc_ptr,否则它将如何释放适当的内存量?因此,运行时知道必须释放多少个对象。
Giorgio

4

您显示的琐碎情况可以在编译时检测到,因为对象的实例化和销毁在同一范围内。通常,删除与实例化不在同一范围内,甚至在同一源文件中。C ++指针的类型不携带有关它是引用其类型的单个对象还是数组的信息,更不用说分配方案了。因此,通常不可能在编译时对此进行诊断。

为什么不诊断可能的特殊情况?

在C ++中,已经存在用于处理与范围相关联的动态资源泄漏的工具,即智能指针和更高级别的数组(std::vector)。

即使您使用正确的delete样式,您的代码仍然不是异常安全的。如果之间的代码new[],并delete[]通过动态退出终止,删除从未执行。

就运行时检测而言,该Valgrind工具可以很好地在运行时进行检测。看:

==26781== Command: ./a.out
==26781==
==26781== Mismatched free() / delete / delete []
==26781==    at 0x402ACFC: operator delete(void*) (in /usr/lib/valgrind/vgpreload_memcheck-x86-linux.so)
==26781==    by 0x8048498: main (in /home/kaz/test/a.out)
==26781==  Address 0x4324028 is 0 bytes inside a block of size 80 alloc'd
==26781==    at 0x402B454: operator new[](unsigned int) (in /usr/lib/valgrind/vgpreload_memcheck-x86-linux.so)
==26781==    by 0x8048488: main (in /home/kaz/test/a.out)

当然,Valgrind并不能在所有平台上运行,并且在该工具下重现所有运行时情况并不总是可行或可行的。


您说这种琐碎的情况可以在编译时检测到。您能告诉我您使用什么编译命令来实现吗?
SebGR

这里的“可以在编译时检测到”表示在编译器中很容易实现,而不是g ++拥有它。编译器在处理该范围时将掌握标识符的整个生命周期,并且可以将分配信息作为与语法绑定的语义属性进行传播。
卡兹(Kaz)

-3

在编译时/静态分析时进行检测的一些简单示例:

在RHEL7主机上 cppcheck 1.77 and 1.49

> cat test.cc
#include <memory>
int main(){char* buf = new char[10];delete buf;}

http://cppcheck.sourceforge.net/

> cppcheck -x c++ test.cc
Checking test.cc ...
[test.cc:2]: (error) Mismatching allocation and deallocation: buf

随着clang++ 3.7.1上RHEL7

> clang++ --analyze -x c++ test.cc
test.cc:2:37: warning: Memory allocated by 'new[]' should be deallocated by
'delete[]', not 'delete'
int main(){char* buf = new char[10];delete buf;}
                                    ^~~~~~~~~~
1 warning generated.

Clang静态分析器还可以检测std::unique_ptr未通过的时间<char[]>

> cat test2.cc
#include <memory>
int main(){std::unique_ptr<char> buf(new char[10]);}

https://clang-analyzer.llvm.org/

> clang++ --analyze -x c++ -std=c++11 test2.cc
In file included from test2.cc:1:
In file included from /opt/rh/devtoolset-4/root/usr/lib/gcc/x86_64-redhat-linux/5.3.1/
../../../../include/c++/5.3.1/memory:81:
/opt/rh/devtoolset-4/root/usr/lib/gcc/x86_64-redhat-linux/5.3.1/
../../../../include/c++/5.3.1/bits/unique_ptr.h:76:2: 
warning: Memory allocated by
      'new[]' should be deallocated by 'delete[]', not 'delete'
        delete __ptr;
        ^~~~~~~~~~~~
1 warning generated.

在下面进行更新,并提供将其添加到clang的工作,测试和我发现的一个错误的链接。

这是通过reviews.llvm.org/D4661添加到clang的:“检测不匹配的'new'和'delete'用法”

测试位于test / Analysis / MismatchedDeallocator-checker-test.mm中

我发现了此打开的错误-bugs.llvm.org/show_bug.cgi?id=24819


没人怀疑您可以找到一种检测一种特定错误用法的静态分析仪,而不是一种检测所有错误用法的静态分析仪(并希望能误认为任何正确用法)
Caleth
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.