I’d like to know if it’s possible to detect the delete
error commented below at compile time? Especially, I’d like to hear about g++ compiler.
ClassTypeA *abc_ptr = new ClassTypeA[100];
abc_ptr[10].data_ = 1;
delete abc_ptr; // error, should be delete []
6
In general, the compiler can’t detect such errors. Example: Suppose the constructor for some class allocates some data member using new TypeName[]
, but the destructor erroneously uses delete
instead of delete[]
. If the constructor and destructor are defined in separate compilation units, how is the compiler to know when compiling the file that defines the destructor that the usage is inconsistent with that in the separately compiled file that defines the constructor?
With regard to the GNU compilers, it doesn’t. As noted above, it can’t do so in the general case. A compiler doesn’t have to detect such mismatched new/delete errors because this is undefined behavior. UB is the compiler vendor’s “get out of jail free” card.
Tools such as valgrind can and do detect these kinds of new/delete mismatches, but do so at runtime. There might be a static analysis tool that looks at all of the source files that will eventually be compiled to form an executable, but I don’t of any such static analysis tool that detect this kind of error.
1
You can use the appropriate RAII classes to delete
. This is the only safe way of doing it, and this error is only one of the very, very many you will encounter calling delete
yourself.
Always use classes to manage dynamic lifetime resources, and the type system will enforce correct resource destruction.
Edit: “What if you’re auditing the code and can’t change it?” You’re fucked.
25
This particular error – yes. This kind of error generally: unfortunately, no! That would involve predicting the flow of execution without actually executing it, and that isn’t possible for arbitrary programs. (That’s why most compilers don’t even try to detect simple cases like your example.)
Therefore DeadMG’s answer is the appropriate one: don’t try to get it right by paying attention – human attention is fallible. Use the language-provided means and let the computer pay attention.
12
The trivial case which you show can be detected at compile time, because the instantiation and destruction of the object are in the same scope. In general, the deletion is no in the same scope, or even the same source file, as the instantiation.
And a C++ pointer’s type does not carry information about whether it references a single object of its type or an array, let alone the allocation scheme. So it is not possible to diagnose this at compile time in general.
Why not diagnose the special cases that are possible?
In C++ there are already tools for dealing with leakage of dynamic resources that are tied to scopes, namely smart pointers and higher level arrays (std::vector
).
Even if you use the correct delete
flavor, your code is still not exception safe. If the code between the new[]
and delete[]
terminates by a dynamic exit, the deletion never executes.
As far as run-time detection goes, the Valgrind
tool does a good job of detecting this at run-time. Watch:
==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)
Of course, Valgrind doesn’t run on all platforms, and it’s not always practical or possible to reproduce all run-time situations under the tool.
2
Some trivial examples of detection at compile-time / static-analysis-time:
On a RHEL7 host with 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
With clang++ 3.7.1
on 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.
The Clang Static Analyzer can also detect when std::unique_ptr
is not passed <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.
Update below with a link to the work that added this to clang, the tests and one bug I found.
This was added to clang with reviews.llvm.org/D4661 – “Detect mismatching ‘new’ and ‘delete’ uses”.
Tests are in test/Analysis/MismatchedDeallocator-checker-test.mm
I found this open bug – bugs.llvm.org/show_bug.cgi?id=24819
1