コンパイル時の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
@ジェームス:はい。キーは「手動」です。
マーティンヨーク

時には、レガシーコードの多くを書き換え伴うだろうこれを順守する
ニック・キースリー

使用std::unique_ptr<ClassTypeA[]>してからする必要はありません。
user253751

回答:


6

一般に、コンパイラはそのようなエラーを検出できません。例:クラスのコンストラクターがを使用してデータメンバーを割り当てるnew TypeName[]が、デストラクタがのdelete代わりに誤って使用するとしdelete[]ます。コンストラクタとデストラクタが別々のコンパイル単位で定義されている場合、デストラクタを定義するファイルをコンパイルするときに、コンストラクタを定義する個別にコンパイルされたファイルの使用と矛盾することをコンパイラはどのように知るのですか?

GNUコンパイラに関しては、そうではありません。上記のように、一般的なケースではできません。これは未定義の動作であるため、コンパイラはこのような不一致の新規/削除エラーを検出する必要はありません。UBは、コンパイラベンダーの「脱獄無料」カードです。

valgrindなどのツールは、このような種類の新規/削除の不一致を検出できますが、実行時に検出します。最終的にコンパイルされて実行可能ファイルを形成するすべてのソースファイルを調べる静的分析ツールがあるかもしれませんが、この種のエラーを検出するような静的分析ツールはありません。


この特定のシナリオのルールを確実に持っているParasoftという静的分析ツールを使用しました。特定のプロジェクトのすべてのファイルで実行されます(正しく構成されている場合)。そうは言っても、Kilian Fothの答えに対するPete Kirkhamのコメントなどのシナリオをどれだけうまく処理できるかはわかりません。
ヴェロキラプトル

28

に適切なRAIIクラスを使用できますdelete。これはそれを行う唯一の安全な方法であり、このエラーは、delete自分自身の呼び出しに遭遇する非常に多くのエラーの1つにすぎません。

常にクラスを使用して、動的な有効期間リソースを管理します。そうすると、型システムは正しいリソース破壊を強制します。

編集:「コードを監査していて変更できない場合はどうなりますか?」あなたはめちゃくちゃです。


18
-1これは実際に質問に答えない
メイソンウィーラー

2
不一致を検出する唯一の方法は、RAIIクラスの使用を伴う型システムを使用することです。
DeadMG

9
...それはさらに意味がありません。RAIIクラス(ランタイムメカニズム)の使用は、コンパイラがコンパイル時に知っている静的型システム情報と何の関係がありますか?
メイソンウィーラー

6
@MasonWheelerは、例としてboost :: shared_ptrおよびboost :: shared_arrayを参照してください。shared_ptrを破壊するとオブジェクトが削除され、shared_arrayが破壊されてアレイが削除されます。shared_arrayをshared_ptrに割り当てることはできません。したがって、最初に配列を使用してshared_ptrを構築しない限り、型システムは間違った削除が使用されるのを防ぎます。
ピートカーカム

4
通常、このような答えは役に立つよりも不快です。ただし、この場合、実際には事実です。彼はコンパイラーが一般的なエラーを強制することを求めており、RAIIを使用することでこのスタイルのエラーを適切に防ぎ、それによって彼が望むものを正確に提供します。+1
riwalk

10

この特定のエラー-はい。通常、こののエラー:残念ながら、ありません!それには、実際に実行せずに実行フローを予測する必要があり、任意のプログラムでは不可能です。(だからほとんどのコンパイラはあなたの例のような単純なケースを検出しようとさえしない。)

したがって、DeadMGの答えは適切なものです。注意を払ってそれを正しくしようとしないでください-人間の注意は誤りやすいです。言語が提供する手段を使用して、コンピューターに注意を向けさせます。


これには、実行の流れを予測することがどのように必要ですか?これは、純粋に静的なコンパイル時の知識のように見えます。コンパイラの型システムは、配列とそうでないものを知っています。
メイソンウィーラー

キャストがいる場合でも?申し訳ありませんが、間違った場合は回答を削除します。
キリアンフォス

12
@MasonWheelerは、abc_ptrの静的型であるClassTypeA*ためif ( rand() % 2 == 1 ) abc_ptr = new ClassTypeA;、静的型システムの新規と削除の間に行を挿入できます。abc_ptr配列または動的オブジェクトを指すか、別のオブジェクトまたは配列の一部を示しています。
ピートカーカム

...そうそう。私は実際の配列型の言語で作業することに慣れているので、Cランドでそれがどのように台無しにされたかを忘れがちです。:(
メイソンウィーラー

1
@Pete Kirkham、@ Mason Wheeler:それでも、ランタイムは、が指すアドレスに格納されているオブジェクトの数を確認する必要がabc_ptrあります。そのため、ランタイムは割り当て解除する必要のあるオブジェクトの数を把握しています。
ジョルジオ

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 ++が持っているのではなく、コンパイラで簡単に実装できることを意味します。コンパイラは、そのスコープを処理するときに識別子の有効期間全体を把握し、構文に関連付けられたセマンティック属性として割り当て情報を伝達できます。
カズ

-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.1RHEL7上

> 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 Static Analyzer 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に追加した作業、テスト、見つかった1つのバグへのリンクで以下を更新します。

これはreviews.llvm.org/D4661- "不一致の 'new'と 'delete'の使用を検出することでclangに追加されました。

テストはtest / Analysis / MismatchedDeallocator-checker-test.mmにあります

このバグを発見しました-bugs.llvm.org/show_bug.cgi?id=24819


あなたが検出し、静的アナライザを見つけることができません-1の疑問一つの特定の間違った使い方を、検出した代わりに、1 すべての間違った使い方(そして、できればミスなし右用法を)
Caleth
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.