145

よし、次のコードで何が起こるかは未定義であることに誰もが同意すると思う。

void deleteForMe(int* pointer)
{
     delete[] pointer;
}

ポインターはあらゆる種類のものである可能性があるため、無条件で実行するdelete[]ことは未定義です。ただし、実際に配列ポインターを渡していると仮定しましょう。

int main()
{
     int* arr = new int[5];
     deleteForMe(arr);
     return 0;
}

私の質問は、ポインタが配列であるこの場合、これを知っているのは誰ですか? つまり、言語/コンパイラの観点からarrは、配列ポインターか単一の int へのポインターかどうかはわかりません。arr一体、動的に作成されたかどうかさえわかりません。それでも、代わりに次のことを行うと、

int main()
{
     int* num = new int(1);
     deleteForMe(num);
     return 0;
}

OS は、int を 1 つだけ削除し、その時点以降のメモリの残りを削除することによって、ある種の「大量殺戮」を行わないほど十分にスマートです (これを終了しstrlenていない\0文字列と比較してください。ヒット 0)。

では、これらのことを覚えるのは誰の仕事でしょうか? OS は何らかの種類の記録をバックグラウンドで保持していますか? (つまり、何が起こるかは未定だと言ってこの投稿を始めたことに気づきましたが、実際には、「殺戮」シナリオは起こらないので、実際の世界では誰かが覚えています。)

4

16 に答える 16

105

コンパイラはそれが配列であることを認識せず、プログラマを信頼しています。単一のintwithへのポインターを削除するdelete []と、未定義の動作が発生します。2 番目のmain()例は、すぐにクラッシュしなくても安全ではありません。

コンパイラは、何らかの形で削除する必要があるオブジェクトの数を追跡する必要があります。これは、配列サイズを格納するのに十分な量を過剰に割り当てることによって行われる場合があります。詳細については、C++ スーパー FAQを参照してください。

于 2009-04-01T01:30:15.450 に答える
30

はい、OS は「バックグラウンド」でいくつかのことを保持します。たとえば、実行すると

int* num = new int[5];

OS は追加の 4 バイトを割り当て、割り当てられたメモリの最初の 4 バイトに割り当てのサイズを格納し、オフセット ポインタを返すことができます (つまり、メモリ空間 1000 から 1024 を割り当てますが、返されるポインタは 1004 を指し、位置は 1000 ~ 1003 割り当てのサイズを格納します)。次に、delete が呼び出されると、ポインターが渡される前の 4 バイトを調べて、割り当てのサイズを見つけることができます。

割り当てのサイズを追跡する方法は他にもあると思いますが、それは 1 つのオプションです。

于 2009-04-01T01:34:01.463 に答える
13

これはこの質問と非常によく似ており、探している詳細の多くが含まれています。

ただし、これを追跡するのは OS の仕事ではありません。実際には、配列のサイズを追跡するのは、ランタイム ライブラリまたは基になるメモリ マネージャーです。これは通常、前もって余分なメモリを割り当て、その場所に配列のサイズを格納することによって行われます (ほとんどの場合、ヘッド ノードが使用されます)。

これは、次のコードを実行することにより、一部の実装で表示できます

int* pArray = new int[5];
int size = *(pArray-1);
于 2009-04-01T01:33:45.130 に答える
9

deleteまたはdelete[]おそらく両方とも割り当てられたメモリを解放します(メモリが指す)が、大きな違いはdelete、配列では配列の各要素のデストラクタを呼び出さないことです。

とにかく混ぜnew/new[]て、delete/delete[]多分UBで​​す。

于 2009-04-16T10:44:09.527 に答える
6

これと同様の質問がありました。C では、malloc() (または別の同様の関数) でメモリを割り当て、free() で削除します。malloc() は 1 つだけで、単純に特定のバイト数を割り当てます。free() は 1 つだけで、単純にポインターをパラメーターとして受け取ります。

では、C ではポインタを free に渡すだけでよいのに、C++ ではそれが配列か単一変数かを指定しなければならないのはなぜでしょうか?

私が学んだ答えは、クラスのデストラクタに関係しています。

クラス MyClass のインスタンスを割り当てると...

classes = new MyClass[3];

そしてdeleteで削除すると、呼び出されたMyClassの最初のインスタンスのデストラクタのみを取得できます。delete[] を使用すると、配列内のすべてのインスタンスに対して確実にデストラクタが呼び出されます。

これが重要な違いです。単純に標準型 (int など) を使用している場合、この問題は実際には発生しません。さらに、new[] で delete を使用し、new で delete[] を使用した場合の動作は定義されていないことを覚えておく必要があります。すべてのコンパイラ/システムで同じように機能するとは限りません。

于 2010-01-28T00:46:46.183 に答える
6

それが配列であることを知らないためdelete[]、通常の old の代わりに指定する必要がありますdelete

于 2009-04-01T01:31:22.530 に答える
3

free を使用して標準 C で malloc で作成された配列を削除できるのと同じように、メモリ割り当てを担当するのはランタイム次第です。コンパイラごとに実装が異なると思います。一般的な方法の 1 つは、配列サイズに余分なセルを割り当てることです。

ただし、ランタイムは、それが配列かポインターかを検出するほどスマートではありません。通知する必要があり、間違っている場合は、正しく削除しないか (たとえば、配列ではなく ptr)、またはサイズに関係のない値を取得することになり、重大な損傷を引き起こします。

于 2009-04-01T01:32:35.040 に答える
1

配列であるかどうかをコンパイラが認識していないことに同意します。プログラマ次第です。

コンパイラは、配列サイズを格納するのに十分な量を過剰に割り当てることによって、削除する必要があるオブジェクトの数を追跡することがありますが、常に必要というわけではありません。

追加のストレージが割り当てられる場合の完全な仕様については、C++ ABI (コンパイラの実装方法) を参照してください: Itanium C++ ABI: Array Operator new Cookies

于 2011-05-06T09:49:48.983 に答える
1

意味的には、C++ の両方のバージョンの削除演算子は、任意のポインターを「食べる」ことができます。ただし、単一のオブジェクトへのポインタが に与えられた場合はdelete[]、結果として UB が発生します。つまり、システム クラッシュや何も発生しないなど、あらゆることが発生する可能性があります。

C++ では、解放の対象 (配列または単一オブジェクト) に応じて、適切なバージョンの削除演算子をプログラマが選択する必要があります。

削除演算子に渡されたポインターがポインター配列であるかどうかをコンパイラーが自動的に判断できる場合、C++ には削除演算子が 1 つしかないため、どちらの場合でも十分です。

于 2010-01-28T00:54:35.230 に答える
0

配列にはdeleteを使用できません。また、非配列にはdelete[]を使用できません。

于 2009-04-10T17:18:49.590 に答える