21

メモリの動的割り当てで少し遊んでいますが、ポイントがわかりません。ステートメントでメモリを割り当てるときnew、ポインターが指しているメモリを使用して破棄できるはずですdelete

しかし、試してみると、deleteポインターが指しているスペースが空になっていないように見えるため、このコマンドは機能していないようです。

例として、この本当に基本的なコードを見てみましょう。

#include <iostream>  

using namespace std;

int main()  
{  
    //I create a pointer-to-integer pTest, make it point to some new space,  
    // and fulfill this free space with a number;  
    int* pTest;  
    pTest = new int;  
    *(pTest) = 3;  
    cout << *(pTest) << endl; 

    // things are working well so far. Let's destroy this
    // dynamically allocated space!
    delete pTest;

    //OK, now I guess the data pTest pointed to has been destroyed 
    cout << *(pTest) << endl; // Oh... Well, I was mistaking.  

    return 0;  
}  

どんな手掛かり ?

4

9 に答える 9

60

未定義の動作とは何かを学ぶ時が来ました。:)

C++ では、違法/無意味な/悪い/その他のことをしたとき。標準では、「未定義の動作につながる」とよく言われます。これは、その時点以降、プログラムの状態は完全に保証されておらず、何かが起こる可能性があることを意味します.

最後の を実行した時点で、*(pTest)未定義の動作が発生します。これは、pTestが有効なオブジェクトを指しておらず、そのようなポインターの逆参照が定義されていないためです。つまり、未定義の出力が完全に許可されています。

あなたがしたことは、「この割り当ては終了しました」と言われただけです。それを言ったら、そのメモリを検査したり気にしたりするべきではありません (そして、実際にはできません)。何かの割り当てを解除してから使用しようとすることは、概念的な意味さえありません。あなたは終わったと言った!

ただし、出力はある程度予測可能です。おそらく、OSは単に「わかりました、メモリをありがとう」と言って、それだけです。実際にメモリを「リセット」したり、特別なことをしたりする理由はありません。誰も(あなた自身のプログラムを含む)それを使用していない場合、それは確かに時間の無駄です。

ただし、この出力は完全に定義されていないことに注意してください。存在しないオブジェクトを使用しようとしないでください。おそらく、より良いテストは次のとおりです。

#include <iostream>

struct foo
{
    ~foo()
    {
        std::cout << "foo is gone :(" << std::endl;
    }
};

int main(void)
{
    foo* f = new foo();
    delete f; // you'll see that the object is destroyed.
}

メモリ自体で何が起こるかを見ようとしていたようですが。メモリを削除してから使用しようとしても意味がないことを覚えておいてください。答えは次のとおりです。それは、C++ が気にしない特定のプラットフォーム次第です。

于 2010-07-19T11:02:20.537 に答える
11

delete を呼び出すと、メモリ領域が空きとしてマークされます。古い値をリセットする必要はありません。

delete を呼び出した後、ポインタを 0 に設定することをお勧めします。

delete pTest;
pTest = 0;
于 2010-07-19T11:01:22.267 に答える
5

delete 演算子は、オブジェクトのデストラクタを呼び出し、以前にオブジェクトに割り当てられたメモリの割り当てを解除します。削除されたオブジェクトを指すポインター変数には影響しません。

したがって、破棄されたオブジェクトを指すポインターを逆参照すると、問題が発生します。

于 2010-07-19T10:57:53.113 に答える
5

答えはパフォーマンスです。

0xCCCCCCCC解放されたすべてのメモリを無効な値 ( 、0xDEADDEADなど) で埋めて、既に解放されたメモリへの古いポインタを使用しようとする試みをキャッチすることは、デバッグに非常に役立ちます。

ただし、解放されたメモリを変更すると CPU 時間がかかるため、パフォーマンス上の理由から、OS は解放されたメモリ ブロックを「空き」リストに追加し、内容をそのまま残します。

于 2010-07-19T11:27:02.373 に答える
3

解放されたメモリを指すポインターの逆参照は、未定義の動作です。

newによって提供されるメモリは通常、アロケータが管理する割り当てられたメモリのより大きなチャンクの一部であるため、多くの場合、それは機能します。を呼び出すdeleteと、関連するデストラクタが呼び出され、メモリが空きとしてマークされます。これは通常、「再利用の準備ができている」ことを意味します。したがって、そのメモリを調べると、 への呼び出し前に存在していたのと同じデータdelete、またはそのメモリのチャンクが呼び出し後に再割り当てされている場合は他のデータが見つかりnewます。

new/アロケータが OS 仮想メモリ関数の薄いラッパーとして機能することを禁止するものは何もないことに注意してください。そのdeleteため、ページに関連して割り当てられたすべてのブロックが割り当て解除されると、ページ全体が解放され、それにアクセスしようとするとアドレス違反が発生します。 .

TL、DR バージョン: 割り当て解除されたメモリを指すポインタを参照しないでください: 動作する場合もあれば、ガベージが返される場合もあれば、アクセス違反が発生する場合もあります。

この種の間違いをしている場合にすぐに気付く良い方法は、ポインタが指すメモリを削除した後にポインタを NULL に設定することです。コードが NULL ポインタを逆参照しようとすると、ほとんどのシステムでアプリケーションがクラッシュします。 、そのため、このような障害が見過ごされることはありません。

于 2010-07-19T11:07:50.790 に答える
1

データを破棄するとはどういう意味ですか? 私はそれをゼロにすることができると思いますが、なぜ気にするのですか?環境から取得したときは汚れていると想定されますが、返却する前にクリーニングする必要はありません。私たちはそれを読む権利を放棄しているので、その内容は気にしません。また、delete がポインター自体をゼロにしない理由については、次のとおりです。

http://www2.research.att.com/~bs/bs_faq2.html#delete-zero

于 2010-07-19T11:05:55.990 に答える
1

何が起こるかを説明するための簡単な例と、一部の人々が言及した未定義の動作が何を意味するかを示します。

出力の前に 2 行のコードを追加すると、次のようになります。

delete pTest;

int *foo = new int;
*foo = 42;

cout << *pTest << endl;

あなたの場合のように、pTestの出力値は3になる可能性が非常に高いです。ただし、出力される値は 42 になることもあります。pTest ポインターが削除されると、そのメモリが解放されます。このため、foo ポインターは、削除される前に pTest が指していたメモリ内の同じ場所を指す可能性があります。

于 2010-07-19T11:25:21.463 に答える
1

マップされたメモリの任意の部分を参照できた可能性があります。または、プログラムの実行時間、メモリ割り当ての詳細、およびライブラリが後で OS にメモリを返すかどうかによっては、マップされていないメモリが発生する可能性があります...

削除されているすべてのメモリを実際にクリアするとdelete、遅かれ早かれ上書きされる可能性のあるメモリをスクラブするのに多くの時間を浪費するため、プログラムの実行時間が大幅に長くなります。デバッグには適しているかもしれませんが、本番環境での使用では、メモリの内容を実際にスクラブする必要はほとんどありません。(もちろん、暗号鍵は良い例外です。or を呼び出す前にこれらをスクラブすることをdeletefree勧めします。)

于 2010-07-19T11:04:38.487 に答える