削除[]それは、アレイのどのように知っているのですか?
-
22-08-2019 - |
質問
さてさて、私は、我々はすべてのものを次のコードで起こることに同意考えている未定義、渡されたものに応じて、
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型を削除し、(その点を超えるコントラストをメモリの残りの部分を削除して「殺しまくる」のいくつかの種類に行かないように十分にスマートであることstrlen
と非\0
で終了する文字列で - それはそれが0に達するまで)続けるでしょう。
だから、その仕事、それはこれらの事を覚えているのですか? OSは、バックグラウンドでのレコードのいくつかの種類を維持していますか? (私が意味する、私は何が起こることは未定義であると言って、このポストを始めたことを認識し、実際には「殺しまくる」シナリオはそのための実用的な世界では、の誰かのある、発生しません、です覚え。)
解決
、それはプログラマを信頼しています。 int
で単一delete []
へのポインタを削除すると、未定義の動作につながります。あなたの二main()
の例では、それがすぐにクラッシュしていない場合でも、安全ではありません。
コンパイラが何らかの形で削除する必要がどのように多くのオブジェクトを追跡する必要がありません。これは、過剰割り当て、配列のサイズを格納するのに十分でこれを行うことができます。詳細については、 C ++スーパーよくある質問を見ますます。
他のヒント
これまでに与えられた答えは、対処していないようだという一つの疑問:ランタイムライブラリ(いないOSは、本当に)配列で物事の数を追跡することができる場合は、なぜ我々はdelete[]
構文でが必要なのかすべて?なぜ単一delete
フォームは、すべての削除を処理するために使用することができないのですか?
これに対する答えはC互換の言語としてC ++のルーツに戻って(それはもはや本当になるよう努めています。)Stroustrup氏の哲学は、プログラマは、彼らが使用していない機能のためにお金を払う必要はありませんということでした。彼らは配列を使用していない場合、それらはメモリのすべての割り当てられたチャンクのオブジェクト配列の費用を運ぶ必要はありません。
つまり、あなたのコードは、単にない場合
Foo* foo = new Foo;
、その後foo
に割り当てられたのメモリ空間はFoo
の配列をサポートするために必要とされるであろう任意の余分なオーバーヘッドを含めるべきではありません。
、あなたは、あなたがオブジェクトを削除すると、その情報を探すためのランタイムライブラリを伝える必要があります。私たちが使用する必要がある理由です。
delete[] bar;
だけではなく、
delete bar;
バーは配列へのポインタである場合。
私達のほとんどのためにメモリのいくつかの余分なバイト程度fussinessは、これらの日趣のある思われること、(私自身は含まれています)。しかし、重要なことができます(メモリブロックの非常に高い数字何ができるかから)数バイトを保存し、まだいくつかの状況があります。
はい、OSは、いくつかの物事を保つ「背景」。たとえば、あなたが実行した場合、
int* num = new int[5];
OSは、4つの余分なバイトを割り当て、割り当てられたメモリの最初の4バイトに割り当てのサイズを記憶し、オフセットポインタ(すなわち、それは1024にメモリ空間1000に割り当てるが、ポインタを用いて、1004にポイントを返さを返すことができ割り当てのサイズを記憶場所1000から1003)。削除が呼び出されたときにポインタが割り当てのサイズを見つけるために、それに渡される前に、次に、それは4つのバイトで見ることができます。
私は、割り当てのサイズを追跡する他の方法があることを確信しているが、それは一つの選択肢だ。
これは、この 質問と、それはあなたが探している詳細の多くを持っています。
しかし、このいずれかを追跡するために、OSの仕事ではない、と言うには十分。それは実際には配列のサイズを追跡するランタイムライブラリまたは基礎となるメモリマネージャです。これは、通常、フロントまで余分にメモリを割り当て、(ほとんどがヘッドノードを使用して)その位置で配列のサイズを記憶することによって行われます。
これは、次のコードを実行することにより、いくつかの実装で視聴可能である
int* pArray = new int[5];
int size = *(pArray-1);
delete
またはdelete[]
は、おそらく両方(メモリが指摘)割り当てられたメモリを解放するだろうが、大きな違いは、アレイ上delete
は、配列の各要素のデストラクタを呼び出すことはありませんということです。
とにかく、new/new[]
とdelete/delete[]
を混合することは、おそらくUBです。
それはあなたが代わりに通常の古いdelete[]
のdelete
を供給する必要が理由です、それは配列の知らない。
私はこれに似た質問がありました。 )(Cでは、あなたは、malloc関数とメモリ()(または他の同様の機能)を割り当て、無料でそれを削除します。単にバイトの一定数を割り当てるだけのmalloc()が、存在します。一つだけの自由は、それがパラメータだとして、単純にポインタをとるには、()があります。
なぜそれはCであなただけ解放するためにポインタを引き渡すことができるということですが、Cにあなたはそれが配列または単一の変数のかどうかということを教えなければなりません++?
答えは、私が学んだ、クラスのデストラクタに関係しています。
あなたはクラスのインスタンスを割り当てる場合はMyClass ...
classes = new MyClass[3];
および削除して、それを削除し、あなただけと呼ばれるのMyClassの最初のインスタンスのデストラクタを得ることができます。あなたは[] deleteを使用する場合は、デストラクタは、アレイ内のすべてのインスタンスのために呼び出されることを保証することができます。
これは重要な違いがあります。あなたは、単に標準タイプ(例えばint型)で作業している場合、あなたは本当にこの問題は表示されません。プラス、あなたは新しい[]を削除し、新しいが定義されていない上、delete []を使用するためにその動作を忘れてはならない - 。それはすべてのコンパイラ/システム上で同じように動作しない場合があります。
それはあなたが自由に使用して、標準のCでのmallocで作成したアレイを削除することができます同じように、メモリの割り当てを担当してランタイム次第です。私は、それぞれのコンパイラが異なり、それを実装してだと思います。一般的な方法の1つは、配列サイズのための余分なセルを割り当てることです。
ただし、ランタイムはそれが配列またはポインタであるか否かを検出するために十分にスマートではありません、あなたはそれを通知しなければならない、とあなたが間違っている場合、あなたは正しく削除しないのいずれか(例えば、PTR配列の代わりに)、あなたはサイズに関係のない値を撮って終了し、重大な損傷を引き起こしますか。
コンパイラのためのアプローチの一つは、ヘッド要素内の要素の少しより多くのメモリおよび格納数を割り当てることです。
これを行うことができる方法の例: ここで
int* i = new int[4];
コンパイラは、* 5バイトのsizeof(int)を割り当てます。
int *temp = malloc(sizeof(int)*5)
最初の4
バイトにsizeof(int)
格納します。
*temp = 4;
と設定i
i = temp + 1;
4つの要素ではなく、5のアレイにそこでi
点
と
delete[] i;
の方法以下の処理されます。
int *temp = i - 1;
int numbers_of_element = *temp; // = 4
... call destructor for numbers_of_element elements if needed
... that are stored in temp + 1, temp + 2, ... temp + 4
free (temp)
意味的に、C ++でdelete演算子の両方のバージョンは、任意のポインタを「食べる」ことができます。単一のオブジェクトへのポインタがdelete[]
するために与えられている場合しかし、その後、UBは、システムのクラッシュや、まったく何も含め、何かが起こるかもしれないという意味、なります。
C ++が割り当て解除の対象に応じて削除オペレータの適切なバージョンを選択するために、プログラマが必要です。配列または単一のオブジェクト
コンパイラが自動的に削除オペレータに渡されたポインタは、ポインタ配列であったかどうかを決定することができる場合には、両方の場合のために十分であるC ++で唯一delete演算子、が存在するであろう。
は、それが配列であるかどうかをコンパイラが知らないことに同意します。これは、プログラマ次第です。
コンパイラは、時には過剰に割り当てる配列サイズを格納するのに十分で削除する必要がありますどのように多くのオブジェクトを追跡するが、必ずしも必要ではありません。
完全な仕様については、余分なストレージが割り当てられた場合、C ++ ABIを参照して下さい(コンパイラの実装方法):<のhref = "http://www.codesourcery.com/public/cxx-abi/abi.html#配列-クッキー」のrel = "nofollowを"> ItaniumのC ++ ABI:配列演算子の新しいクッキーの
あなたは、配列のためのを削除の使用することはできませんし、使用することはできません。<全角>を削除[] 。
「未定義の動作は、」単に言語仕様は何が起こるかについて何らgauranteesをしないことを意味します。それはnessacerally悪い何かが起こるだろうことを意味するものではありません。
だから、その仕事、それはこれらの事を覚えているのですか? OSは、バックグラウンドでのレコードのいくつかの種類を維持していますか? (私が意味する、私は何が起こることは未定義であることを言って、このポストを始めたが、実際は、「殺しまくる」シナリオが発生しませんので、そのための実用的な世界では誰かが覚えていることを認識しています。)
ここでは2つの層は一般的にあります。基礎となるメモリマネージャおよびC ++の実装ます。
は、一般に、メモリマネージャは、割り当てられたメモリのブロックの大きさ(とりわけ)覚えているだろう。これは、C ++の実装がを求めたブロックよりも大きくすることができます。典型的には、メモリマネージャは、メモリの割り当てブロックの前にメタデータのストアであろう。
それはそれ自身の目的のためにそうする必要がある場合は、C ++の実装は、一般的に唯一のタイプは非trivalデストラクタを持っている通常ので、配列のサイズを覚えてます。
些細なデストラクタを持つタイプのためだから「[]削除」「削除」の実装は、一般的に同じです。 C ++実装は、単に下層のメモリマネージャにポインタを渡します。
のようなものfree(p)
非自明なデストラクタを持つタイプの一方で「削除」と異なる可能性がある「[]削除」。 「削除」など気にいらあろう(Tタイプである場合、そのポインタが指す)
p->~T();
free(p);
"を削除[]" のようなものであろうが。
size_t * pcount = ((size_t *)p)-1;
size_t count = *count;
for (size_t i=0;i<count;i++) {
p[i].~T();
}
char * pmemblock = ((char *)p) - max(sizeof(size_t),alignof(T));
free(pmemblock);
ちょっとHOはよくそれは(あなたがタイプまたはクラス/構造でビルドの配列を割り当て、あなたのコンストラクタを提供しないと、オペレータはサイズ」のsizeofとして扱いますデストラクタときは、新しい[]式で配分何に依存しますあなたがオブジェクト配列を割り当て、あなたが行動の変化よりも、あなたのオブジェクト内のコンストラクタとデストラクタを提供ただし場合オブジェクト)* numObjects」のではなく、オブジェクトの配列したがって、この場合には、割り当てられたオブジェクトの数はどこにも保存されません、新しい式が4バイト以上を割り当てますし、最初の4バイトのオブジェクトの店舗数は、それらのそれぞれのデストラクタに呼び出すことができ、したがって、新たな[]式はメモリが返された場合よりも、前方に4バイトだけシフトポインタを戻します削除[]式が関数テンプレートを呼び出しますまず、オブジェクトの配列を反復処理し、それらのそれぞれに対してデストラクタを呼び出します。私はこの単純なコードの魔女が新しい[]オーバーロードと削除[]表現やメモリを解放し、必要に応じて各オブジェクトのデストラクタを呼び出すためにテンプレート機能を提供して作成しました。
// overloaded new expression
void* operator new[]( size_t size )
{
// allocate 4 bytes more see comment below
int* ptr = (int*)malloc( size + 4 );
// set value stored at address to 0
// and shift pointer by 4 bytes to avoid situation that
// might arise where two memory blocks
// are adjacent and non-zero
*ptr = 0;
++ptr;
return ptr;
}
//////////////////////////////////////////
// overloaded delete expression
void static operator delete[]( void* ptr )
{
// decrement value of pointer to get the
// "Real Pointer Value"
int* realPtr = (int*)ptr;
--realPtr;
free( realPtr );
}
//////////////////////////////////////////
// Template used to call destructor if needed
// and call appropriate delete
template<class T>
void Deallocate( T* ptr )
{
int* instanceCount = (int*)ptr;
--instanceCount;
if(*instanceCount > 0) // if larger than 0 array is being deleted
{
// call destructor for each object
for(int i = 0; i < *instanceCount; i++)
{
ptr[i].~T();
}
// call delete passing instance count witch points
// to begin of array memory
::operator delete[]( instanceCount );
}
else
{
// single instance deleted call destructor
// and delete passing ptr
ptr->~T();
::operator delete[]( ptr );
}
}
// Replace calls to new and delete
#define MyNew ::new
#define MyDelete(ptr) Deallocate(ptr)
// structure with constructor/ destructor
struct StructureOne
{
StructureOne():
someInt(0)
{}
~StructureOne()
{
someInt = 0;
}
int someInt;
};
//////////////////////////////
// structure without constructor/ destructor
struct StructureTwo
{
int someInt;
};
//////////////////////////////
void main(void)
{
const unsigned int numElements = 30;
StructureOne* structOne = nullptr;
StructureTwo* structTwo = nullptr;
int* basicType = nullptr;
size_t ArraySize = 0;
/**********************************************************************/
// basic type array
// place break point here and in new expression
// check size and compare it with size passed
// in to new expression size will be the same
ArraySize = sizeof( int ) * numElements;
// this will be treated as size rather than object array as there is no
// constructor and destructor. value assigned to basicType pointer
// will be the same as value of "++ptr" in new expression
basicType = MyNew int[numElements];
// Place break point in template function to see the behavior
// destructors will not be called and it will be treated as
// single instance of size equal to "sizeof( int ) * numElements"
MyDelete( basicType );
/**********************************************************************/
// structure without constructor and destructor array
// behavior will be the same as with basic type
// place break point here and in new expression
// check size and compare it with size passed
// in to new expression size will be the same
ArraySize = sizeof( StructureTwo ) * numElements;
// this will be treated as size rather than object array as there is no
// constructor and destructor value assigned to structTwo pointer
// will be the same as value of "++ptr" in new expression
structTwo = MyNew StructureTwo[numElements];
// Place break point in template function to see the behavior
// destructors will not be called and it will be treated as
// single instance of size equal to "sizeof( StructureTwo ) * numElements"
MyDelete( structTwo );
/**********************************************************************/
// structure with constructor and destructor array
// place break point check size and compare it with size passed in
// new expression size in expression will be larger by 4 bytes
ArraySize = sizeof( StructureOne ) * numElements;
// value assigned to "structOne pointer" will be different
// of "++ptr" in new expression "shifted by another 4 bytes"
structOne = MyNew StructureOne[numElements];
// Place break point in template function to see the behavior
// destructors will be called for each array object
MyDelete( structOne );
}
///////////////////////////////////////////
ちょうどクラス内のデストラクタを定義して、両方の構文を使用してコードを実行
delete pointer
delete [] pointer
uは解決策を見つけることができます出力に応じて
答えます:
INT * pArray =新しいINT [5];
のintサイズ= *(pArray-1)
上記の投稿は正しくないと無効な値を生成します。 「-1」の要素を数えます 64ビットのWindowsは、正しいバッファサイズのPtrに存在する一般OS - 4バイトアドレス