delete []はどのようにしてオペランド配列のサイズを「認識」しますか?


250
Foo* set = new Foo[100];
// ...
delete [] set;

配列の境界をに渡さないでくださいdelete[]。しかし、その情報はどこに保存されていますか?標準化されていますか?


sourceforge.net/projects/fastmmはオープンソースであり、memory-managerを置き換えます。ここでは、メモリ管理がどのように機能するか、およびメモリの割り当てと削除の情報がどこから取得されるかを確認できます。

1
FastMMはDelphi / C ++ Builderコンパイラにのみ固有であり、C ++の汎用メモリマネージャではないことに注意してください。C ++でも書かれていません。
レミールボー

回答:


181

ヒープにメモリを割り当てると、アロケータは割り当てたメモリの量を追跡します。これは通常、割り当てられたメモリの直前の「ヘッド」セグメントに格納されます。そうすることで、メモリを解放するときに、デアロケータは解放するメモリの量を正確に把握します。


4
これはC ++での配列割り当てにのみ適用されることに注意してください。他のすべての割り当ては、型のサイズに依存します。一部のライブラリは、通常はデバッグモードでのみ、すべての割り当てサイズを保存します。
Zan Lynx

97
プログラマがこの情報を利用できないようにする理由は絶対にありません。関数へのポインターを渡して解放することはできますが、同じ関数で自分自身のサイズを取得するには、追加のパラメーターを渡さなければなりません。それは意味がありますか?
マークルソン、2009年

26
@Markは、理論的にはアロケーターを解放して、割り当てられたブロックのサイズ(要求されたブロックのサイズとは異なる場合がある)を常に格納するため、少し意味があります。特定のアロケータデザインは、独自の目的でこの情報を必要とする場合や、タイプ情報を使用して非配列ヒープ割り当てのサイズを追跡するのに十分なほど洗練されていない場合があります。配列のサイズを自分で渡す必要がある場合)は少し厄介ですが、考えられるアロケータの設計にパフォーマンスの影響を与える可能性があります。
Doug McClean

33
申し訳ありませんが、この答えは要点を逃しています。QuantumPeteが説明しているのは、基本的には、「free割り当て解除するメモリの量がどのようにわかるか」です。はい、メモリブロックサイズはmalloc(通常はブロック自体)によって「どこかに」格納されfreeます。ただし、new[]/ delete[]は別の話です。後者は基本的にmalloc/の上で動作しfreeます。new[]また、が作成した要素の数を(とは無関係にmalloc)メモリブロックに格納し、後でdelete[]その数を取得して使用して、適切な数のデストラクタを呼び出すことができるようにします。
AnT、2009年

26
つまり、物理的に2つのカウンターがブロックに格納されます:ブロックサイズ(によるmalloc)と要素数(によるnew[])。前者は後者を計算するために使用できないことに注意してください。一般に、メモリブロックのサイズは、要求されたサイズの配列に実際に必要なサイズよりも大きくなる可能性があるためです。また、配列要素カウンタは、自明でないデストラクタを持つ型にのみ必要であることにも注意してください。自明なデストラクタを持つ型の場合、カウンタはによって格納されnew[]ず、もちろんによって取得されませんdelete[]
AnT、2009年

23

コンパイラのアプローチの1つは、もう少し多くのメモリを割り当てて、head要素に要素の数を格納することです。

それがどのようにできるかの例:

ここに

int* i = new int[4];

コンパイラはsizeof(int)*5バイトを割り当てます。

int *temp = malloc(sizeof(int)*5)

最初のsizeof(int)バイトに「4」を格納します

*temp = 4;

そして設定 i

i = temp + 1;

したがってi、5ではなく4要素の配列を指します。

そして削除

delete[] i;

次のように処理されます。

int *temp = i - 1;
int numbers_of_element = *temp; // = 4
... call destructor for numbers_of_element elements
... that are stored in temp + 1, temp + 2, ... temp + 4 if needed
free (temp)

9

情報は標準化されていません。しかし、私が取り組んだプラットフォームでは、この情報は最初の要素の直前にメモリに保存されます。したがって、理論的にはそれにアクセスして検査することができますが、それだけの価値はありません。

また、これは、新しい[]でメモリを割り当てたときに、削除[]を使用する必要がある理由でもあります。削除の配列バージョンは、適切な量のメモリを解放するために、適切な数のデストラクタを呼び出す必要があること(および場所)を知っているためです。オブジェクト用。


5

基本的には次のようにメモリに配置されます:

[info] [あなたが求めたmem ...]

ここで、infoは、割り当てられたメモリの量を格納するためにコンパイラによって使用される構造体であり、そうでないものです。

ただし、これは実装に依存します。



3

これは、コンパイラー固有であるようにC ++標準で定義されています。つまり、コンパイラの魔法です。それは、少なくとも1つの主要なプラットフォームでの重要な配置制限によって破られる可能性があります。

によってdelete[]返されるポインターに対してのみ定義されることを実現することにより、可能な実装について考えることができますnew[]。これは、によって返されるポインターとは異なる場合がありますoperator new[]。実際の実装の1つは、配列カウントをによって返される最初のintに格納しoperator new[]、それをnew[]超えてオフセットされたポインタを返すことです。(これが重要な配置が壊れる理由new[]です。)

operator new[]/operator delete[]!= であることに注意してくださいnew[]/delete[]

さらに、これは、Cがによって割り当てられたメモリのサイズを知る方法と直交していますmalloc


2

なぜなら、「削除」される配列は、「new」演算子の1回の使用で作成されているはずだからです。「新しい」操作は、その情報をヒープに配置する必要があります。そうでなければ、newの追加の使用はどのようにヒープが終了するかを知るでしょうか?


0

標準化されていません。Microsoftのランタイムでは、新しい演算子はmalloc()を使用し、削除演算子はfree()を使用します。したがって、この設定でのあなたの質問は次と同等です:free()はどのようにブロックのサイズを知るのですか?

裏側で、つまりCランタイムで、簿記が行われています。


5
違います。free []を呼び出す前に、delete []はまずデストラクタを呼び出す必要があります。このため、合計割り当てサイズを知るだけでは十分ではありません。実際、new []とdelete []の動作は、VC ++のプレーンタイプとデストラクトタイプで異なります。
須磨

0

これは、最初に考えるよりも興味深い問題です。この応答は、1つの可能な実装に関するものです。

まず、あるレベルではシステムはメモリブロックを「解放」する方法を知っている必要がありますが、基礎となるmalloc / free(通常はnew / delete / new [] / delete []が呼び出す)は必ずしも正確にどのくらいのメモリを覚えているとは限りません要求した場合、切り上げられる可能性があります(たとえば、4Kを超えると、多くの場合、次の4Kサイズのブロックに切り上げられます)。

したがって、メモリブロックのサイズを取得できたとしても、それが小さくなる可能性があるため、新しいメモリ内の値の数はわかりません。したがって、値の数を示す追加の整数を保存する必要があります。

例外として、構築される型にデストラクタがない場合、delete []はメモリブロックを解放する以外に何もする必要がなく、したがって何も格納する必要はありません。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.