41

クラスシングルブロックのインスタンスとして各ブロックを使用して、シンプルで機能するテトリスゲームを作成しました。

class SingleBlock
{
    public:
    SingleBlock(int, int);
    ~SingleBlock();

    int x;
    int y;
    SingleBlock *next;
};

class MultiBlock
{
    public:
    MultiBlock(int, int);

    SingleBlock *c, *d, *e, *f;
};

SingleBlock::SingleBlock(int a, int b)
{
    x = a;
    y = b;
}

SingleBlock::~SingleBlock()
{
    x = 222;
}

MultiBlock::MultiBlock(int a, int b)
{
    c = new SingleBlock (a,b);
    d = c->next = new SingleBlock (a+10,b);
    e = d->next = new SingleBlock (a+20,b);
    f = e->next = new SingleBlock (a+30,b);
}

完全な行をスキャンし、ブロックのリンクリストを実行して、関連するブロックを削除し、->次のポインターを再割り当てする関数があります。

SingleBlock *deleteBlock;
SingleBlock *tempBlock;

tempBlock = deleteBlock->next;
delete deleteBlock;

ゲームは動作し、ブロックは正しく削除され、すべてが想定どおりに機能します。ただし、検査すると、削除されたデータのランダムなビットにアクセスできます。

削除後に削除されたシングルブロックの「x」値をそれぞれprintfすると、ランダムなガベージを返すもの(削除を確認するもの)と222を返すものがあり、デストラクタが呼び出されても、データは実際には削除されていないことがわかります。ヒープ。多くの同一の試行は、適切に削除されないのは常に同じ特定のブロックであることを示しています。

結果:

Existing Blocks:
Block: 00E927A8
Block: 00E94290
Block: 00E942B0
Block: 00E942D0
Block: 00E942F0
Block: 00E94500
Block: 00E94520
Block: 00E94540
Block: 00E94560
Block: 00E945B0
Block: 00E945D0
Block: 00E945F0
Block: 00E94610
Block: 00E94660
Block: 00E94680
Block: 00E946A0

Deleting Blocks:
Deleting ... 00E942B0, X = 15288000
Deleting ... 00E942D0, X = 15286960
Deleting ... 00E94520, X = 15286992
Deleting ... 00E94540, X = 15270296
Deleting ... 00E94560, X = 222
Deleting ... 00E945D0, X = 15270296
Deleting ... 00E945F0, X = 222
Deleting ... 00E94610, X = 222
Deleting ... 00E94660, X = 15270296
Deleting ... 00E94680, X = 222

予想外のデータにアクセスできるのでしょうか?

これが少し長蛇の列になっている場合は申し訳ありません。

4

13 に答える 13

95

予想外のデータにアクセスできるのでしょうか?

これは、技術的には未定義動作として知られています。それがあなたにビールの缶を提供しても驚かないでください。

于 2009-12-18T20:22:29.043 に答える
42

予想外のデータにアクセスできるのでしょうか?

ほとんどの場合、そうです。deleteを呼び出しても、メモリはゼロになりません。

動作は定義されていないことに注意してください。特定のコンパイラを使用すると、メモリがゼロになる場合があります。deleteを呼び出すと、メモリが使用可能としてマークされるため、次に誰かが新しい操作を行うときに、メモリが使用される可能性があります。

あなたがそれについて考えるならば、それは論理的です-あなたがもはやメモリに興味がないことをコンパイラに伝えるとき(deleteを使用して)、なぜコンピュータはそれをゼロにすることに時間を費やす必要があります。

于 2009-12-18T20:22:24.753 に答える
18

削除は何も削除しません。メモリを「再利用できるようになっている」とマークするだけです。他の割り当て呼び出しがそのスペースを予約して埋めるまで、古いデータが保持されます。ただし、それに依存することは大したことではありません。基本的に、何かを削除した場合は、それを忘れてしまいます。

ライブラリでよく見られるこの点に関するプラクティスの1つは、削除関数です。

template< class T > void Delete( T*& pointer )
{
    delete pointer;
    pointer = NULL;
}

これにより、誤って無効なメモリにアクセスするのを防ぐことができます。

を呼び出してもまったく問題がないことに注意してくださいdelete NULL;

于 2009-12-18T20:32:06.420 に答える
10

これは、C++が未定義動作と呼ぶものです。データにアクセスできる場合とできない場合があります。いずれにせよ、それは間違ったことです。

于 2009-12-18T20:22:33.443 に答える
6

ヒープメモリは、一連の黒板のようなものです。あなたが先生だと想像してみてください。あなたがクラスを教えている間、黒板はあなたのものであり、あなたはそれを使ってやりたいことを何でもすることができます。あなたはそれに落書きして、あなたが望むようにものを上書きすることができます。

クラスが終わって部屋を出ようとしているときは、黒板を消す必要のあるポリシーはありません。黒板を次の先生に渡すだけで、通常、書き留めた内容を見ることができます。

于 2009-12-18T21:12:14.590 に答える
3

を介してメモリを解放しても、システムはメモリをクリアしませんdelete()。したがって、メモリが再利用および上書きに割り当てられるまで、コンテンツには引き続きアクセスできます。

于 2009-12-18T20:22:57.110 に答える
3

オブジェクトを削除した後、それが占有していたメモリの内容がどうなるかは定義されていません。これは、メモリを自由に再利用できることを意味しますが、実装は元々そこにあったデータを上書きする必要はなく、メモリをすぐに再利用する必要もありません。

オブジェクトがなくなった後でメモリにアクセスするべきではありませんが、一部のデータがそのまま残っていることを驚くべきではありません。

于 2009-12-18T20:23:27.527 に答える
1

deleteはメモリの割り当てを解除しますが、変更したりゼロにしたりすることはありません。それでも、割り当て解除されたメモリにアクセスしないでください。

于 2009-12-18T20:22:27.810 に答える
1

はい、それは時々期待することができます。newデータ用のスペースを予約しますが、でdelete作成されたポインターを無効にするだけnewで、以前に予約された場所にデータを書き込むことができます。必ずしもデータを削除するわけではありません。ただし、これらの場所のデータはいつでも変更される可能性があり、プログラムが誤動作する可能性があるため、この動作に依存しないでください。これがdelete、ポインタ(またはdelete[]で割り当てられた配列)で使用した後、そのポインタを使用して、またはそのポインタを再度使用する前にnew[]メモリを割り当てないことを前提として、無効なポインタを改ざんできないようにNULLを割り当てる必要がある理由です。。newnew[]

于 2009-12-18T20:25:54.450 に答える
0

それはまだゼロ/メモリを変更しません...しかし、ある時点で、敷物はあなたの足の下から引っ張られるでしょう。

いいえ、それは確かに予測可能ではありません。それは、メモリの割り当て/割り当て解除がどれだけ速くかき回されるかに依存します。

于 2009-12-18T20:22:20.730 に答える
0

ランタイムがこのエラーを報告しない可能性はありますが、Valgrindなどの適切なエラーチェックランタイムを使用すると、メモリが解放された後にメモリの使用について警告が表示されます。

new/deleteおよびrawポインター(および同様のものではなく)を使用してコードを作成する場合はstd::make_shared()、少なくともそのようなエラーを見つける可能性があるように、Valgrindで単体テストを実行することをお勧めします。

于 2016-08-31T10:03:30.667 に答える
-1

未定義の動作が発生し、メモリの割り当て解除が削除されます。ゼロで再初期化されることはありません。

ゼロにしたい場合は、次のようにします。

SingleBlock::~SingleBlock()

{    x = y = 0 ; }
于 2009-12-18T20:39:22.783 に答える
-3

まあ、私もかなり前からこれについて疑問に思っていました、そして私は内部で何が起こっているのかをよりよく理解するためにいくつかのテストを実行しようとしました。標準的な答えは、deleteを呼び出した後、そのメモリスポットにアクセスすることで何か良いことを期待するべきではないということです。しかし、これは私には十分ではなかったようです。delete(ptr)を呼び出すと、実際に何が起こっているのでしょうか。これが私が見つけたものです。私はUbuntu16.04でg++を使用しているので、これが結果に影響を与える可能性があります。

削除演算子を使用するときに最初に期待したのは、解放されたメモリが他のプロセスで使用するためにシステムに戻されることでした。私が試したどのような状況でも、これは起こらないと言わせてください。

削除で解放されたメモリは、最初にnewで割り当てたプログラムにまだ割り当てられているようです。試しましたが、deleteを呼び出してもメモリ使用量の減少はありません。私は、新しい呼び出しによって約30MBのリストを割り当て、その後の削除呼び出しでそれらを解放するソフトウェアを持っていました。何が起こったのかというと、プログラムの実行中にシステムモニターを見ると、削除呼び出し後の長いスリープでも、プログラムのメモリ消費量は同じでした。減りません!これは、deleteがシステムにメモリを解放しないことを意味します。

実際、プログラムによって割り当てられたメモリは彼の永遠のようです!ただし、重要なのは、割り当てを解除すると、同じプログラムでメモリを再度割り当てることができ、それ以上割り当てる必要がないということです。15MBを割り当てて解放し、その後さらに15MBのデータを割り当てようとしましたが、プログラムが30MBを使用することはありませんでした。システムモニターは常に約15MBを示していました。前のテストに関して私がしたことは、物事が起こった順序を変更することでした:半分の割り当て、半分の割り当て解除、残りの半分の割り当て。

したがって、プログラムで使用されるメモリは増加する可能性がありますが、減少することはありません。使用可能なメモリがなくなったときなど、重大な状況では、他のプロセスのためにメモリが実際に解放されるのではないかと思いました。結局のところ、他のプロセスがそれを要求しているときに、プログラムがそれ自体のメモリを永久に保持できるようにすることは、どのような意味がありますか?そこで、30MBを再度割り当て、割り当てを解除しながらmemtester、できるだけ多くの物理メモリを使用して実行しました。私は自分のソフトウェアがそのメモリをmemtesterに渡すのを見ることを期待していました。しかし、それを推測してください、それは起こりませんでした!

実際の動作を示す短いスクリーンキャストを作成しました。

サンプルメモリを削除する

100%正直に言うと、何かが起こったという状況がありました。プログラムの割り当て解除プロセスの途中で、使用可能な物理メモリを超えるメモリを使用してmemtesterを試したところ、プログラムで使用されるメモリが約3MBに減少しました。しかし、memtesterプロセスは自動的に強制終了され、何が起こったのかはさらに驚くべきことでした。私のプログラムのメモリ使用量は、削除を呼び出すたびに増加しました。それはまるでUbuntuがmemtester事件の後にすべてのメモリを復元しているかのようでした。

http://www.thecrowned.org/c-delete-operator-really-frees-memoryから 取得

于 2017-03-06T15:29:51.033 に答える