web-dev-qa-db-ja.com

C ++ポインターの削除(空きメモリー)

次のコードを検討してください。

int a = 10;
int * b = &a;
int * c = b;
delete b; // equivalent to delete c;

最後の行で理解するのは正しいですか、delete bおよびdelete cは同等であり、どちらもaを保持しているメモリ空間を解放するため、aはアクセスできなくなりますか?

11
space_voyager

プログラムの動作はndefinedです。 onlydeleteを使用して割り当てたメモリへのポインタでnewを使用できます。あなたが書いていた場合

int* b = new int;
*b = 10;
int* c = b;

それからcould write eitherdelete b;またはdelete c;メモリーを解放します。ただし、b呼び出しの後でderefererencecまたはdeleteのいずれかを試みないでください。実行時の動作もndefinedです。

43
Bathsheba

bcが同じメモリを指している場合、どちらかを削除するとメモリが解放されるため、仮定は正しいものになります。 aにアクセスできなくなることは、この場合は正しくありませんが、動的に割り当てられたメモリをポイントせず、delete/delete[]を呼び出すことができるのは、new/new[]delete/delete[]で割り当てられていないポインターをnew/new[]しようとすると未定義の動作になり、通常はセグメンテーションフォールトで終了します。

5
NathanOliver

紛らわしい部分は、あなたの質問に対する答えが

最後の行で理解するのは正しいですか、delete bとdelete cは同等です」

はい、それらは同等であり、両方のUBはここで他の場所で言及されています。

5
UKMonkey

この特定の回答は、ヒープの特定の実装を使用して、提案された状況で何が起こるかについて詳細に説明します。ただし、他のバージョンには、私が提供する引数に近い同様の問題があるため、これはほとんど重要ではありません

答えは「いいえ」であり、他の人が言ったことによるものです。ただし、スタックとヒープが何であるか実際にはわからないと思うので、もう少し詳しく説明します。そのため、時間をかけて説明します。

まず最初に、ヒープの詳細に関して私が言っていることはすべて「嘘」です。コンパイラーがヒープを実装するために何をするのか、具体的には誰も知りません。したがって、代わりにaヒープについてのみ理解できます。

Newとdeleteは、実際には2つの関数です(演算子のオーバーロードを聞いたことがあるなら、これが何を意味するのか理解できます)。これは、ヒープと呼ばれるメモリ内のデータ構造を変更します。 C/C++プログラムのメモリ内の4つの主要なデータ構造(ヒープ、スタック、テキスト、静的空間)の1つ。

ヒープは基本的にリンクリストですが、各ノードにデータを格納する代わりに、それらの間にデータを詰め込みます。リンクされたリストに慣れていない場合、このコンテキストでは、未加工のメモリにある2つのコンポーネントの「配列」です。 2番目のコンポーネントは、その前に格納されているブロックの長さです。最初のコンポーネントは、その前のブロックの長さです。この方法では、リストを下に移動してブロックを検索するだけなので、ブロックを簡単に見つけることができます。通常(プログラムは1つのブロック内で2 ^ 31バイトを割り当てる必要があるほど複雑ではないため)、左端のビットはブロックがフリーかどうかを示すtrue/false値として使用されます。したがって、freeとnewはリストに対する単なる操作であり、freeは単に1つずつポインターをバックアップし、ノードを見て適切な変更を加えます。二重にリンクされたリストに精通している人は操作を理解する必要があるため、これ以上詳しく説明しません。理解できない場合は、後でさらに深く学ぶことを教えようとしても意味がありません。

注意:実装はコンパイラによって異なります。これは単なる例です。

他方のスタックは、ローカル変数がある場所です。文字通り、配列の巨大な配列です。配列の中央には、プログラムが実行後に戻るコード内のアドレスが保持され、パラメーターは一方にあり、ローカル変数は他方にあります。ローカル変数には明確な組織の感覚がありません。一部のコンパイラは、ある値が別の値より先に使用されなくなることを検出し、メモリの場所を共有するだけです(実際にメモリを覗いたり、スタックをスキャンしない限り、気付かないでしょう)。

ご覧のとおり、この2つは意味をなさないだけです。スタックのインデックスを作成するポインターでfreeを呼び出すと、スタックでリスト操作を実行しようとします。次の2つのいずれかが発生します。

  1. 実装は、メモリがヒープ内にないことを検出し、エラーメッセージ(不正な引数例外)で爆発します。

  2. 実装は「ダム」であり、実際にはリスト操作を実行しようとします。これは、2つの空きブロックを1つの大きなブロックに「マージ」しようとして、メモリ内の変数の前の値が変更され、他の値が変更される可能性があることを意味します。

簡単に言えば、それをしないでください。あなたのプログラムで何かを壊すかもしれません。リターンラインを台無しにしたらどうなるでしょうか?次に知っていることは、関数呼び出しの無限ループがまだあるということです...ループは作成していません。ものすごく悪い。

そしてもちろん:

ヒープの正確な実装は、あまり変化しない傾向があるスタックとは異なり、大幅に異なります。

たとえば、そのような構造の未加工メモリのコードや図を見たことはありませんが、解放されたブロックと割り当てられたブロックを保持する2つの独立したリストがあるヒープスペースのモデルがあると聞きました。ただし、割り当てを処理する内部簿記の何らかの形式がまだあるので、これは問題に影響しません。

0
Typhon

スタックに割り当てられた変数にdeleteを使用しないでください。deleteは、newに相当します。したがって、new/allocを使用しない場合、コードの「スコープ」(この場合はプログラム自体)から外れると、すべてのメモリが使用可能と見なされるため、delete/freeは不要です。

0
Nir Levy