これらの2つの宣言に違いはありますか?
int x[10];
vs.
int* x = new int[10];
前者の宣言(後者の宣言と同様)はポインター宣言であり、両方の変数を同じように扱うことができると思います。本質的に同じという意味ですか?
_#include<iostream>
int y[10];
void doSomething()
{
int x[10];
int *z = new int[10];
//Do something interesting
delete []z;
}
int main()
{
doSomething();
}
_
しょー@ sho011221 10 Oct 2018
_int x[10];
_
-スタック上にサイズ10の整数の配列を作成します。
-スタックが巻き戻されるとメモリがなくなるため、このメモリを明示的に削除する必要はありません。
-そのスコープは関数doSomething()
に制限されます
_int y[10];
_
-BSS/Dataセグメントにサイズ10の整数の配列を作成します。
-このメモリを明示的に削除する必要はありません。
-global
と宣言されているため、グローバルにアクセスできます。
_int *z = new int[10];
_
-ヒープにサイズ10整数の動的配列を割り当て、このメモリのアドレスをz
に返します。
-この動的メモリは、使用後に明示的に削除する必要があります。使用:
_delete[] z;
_
の間で似ている唯一のもの
_int x[10];
_
そして
_int* x = new int[10];
_
_int*
_が期待されるいくつかのコンテキストで使用できることです。
_int* b = x; // Either form of x will work
void foo(int* p) {}
foo(x); // Either form will work
_
ただし、_int*
_が期待されるすべてのコンテキストで使用することはできません。具体的には、
_delete [] x; // UB for the first case, necessary for the second case.
_
コアの違いのいくつかは、他の回答で説明されています。その他の主な違いは次のとおりです。
差1
_sizeof(x) == sizeof(int)*10 // First case
sizeof(x) == sizeof(int*) // Second case.
_
差2
_&x
_のタイプは、最初のケースではint (*)[10]
です
_&x
_のタイプは、2番目のケースでは_int**
_です
差
与えられた機能
_void foo(int (&arr)[10]) { }
_
2番目のx
ではなく、最初のx
を使用して呼び出すことができます。
_foo(x); // OK for first case, not OK for second case.
_
標準に従って、実際には3種類の配列宣言を区別する必要があります。
_int x[10];
void method() {
int y[10];
int *z = new int[10];
delete z;
}
_
最初の宣言_int x[10]
_は、 cppreference で定義されるstaticストレージ期間を使用します。オブジェクトのストレージは、プログラムの開始時に割り当てられ、プログラムの終了時に割り当て解除されます。オブジェクトのインスタンスは1つしか存在しません。
2番目の_int y[10]
_は、automaticストレージ期間を使用し、 cppreference で次のように定義されます。オブジェクトは、囲んでいるコードブロックの最初に割り当てられ、最後に割り当て解除されます。静的、extern、またはthread_localとして宣言されたオブジェクトを除き、すべてのローカルオブジェクトにはこの保存期間があります。
3番目の_int *z = new int[10]
_は、通常dynamicメモリ割り当てと呼ばれ、実際には2ステップのシーケンスです。
他のコメントですでに述べたように、これらのタイプの宣言には微妙な違いがありますが、最も一般的なものは次のとおりです。
最新のオペレーティングシステムの場合:
動的に割り当てられたメモリは、プログラマによって明示的にdelete
- edされる必要がありますが、静的および自動ストレージ変数は「環境」によって処理されます
静的および自動ストレージ変数は特定のスコープに制限されますが、動的に割り当てられたメモリには境界がありません。つまり、1つのモジュールで宣言された変数は、同じアドレス空間で動作する他のモジュールに渡すことができます
_new[]
_を使用して配列を割り当てる場合、サイズは0になります
(@R Sahuが既に指摘したように)_&x
_と_&z
_の型は異なります:
&x
_はint (*)[10]
です&z
_は_int **
_です最初は、サイズが_10
_のint
の配列です。スタック上に作成されたと言って間違っています。標準はそれを保証しないためです。その実装定義。その保存期間は、x
がグローバル変数かローカルかどうかに応じて静的または自動になります。変数。
2番目のタイプでは、タイプ_int*
_のポインターを作成します。必ずしもヒープ上に作成されるわけではありませんが、標準ではそうではありません。割り当てられたメモリは10 * sizeof(int)
バイトに及びます。このためには、次のように記述して、自分でメモリの割り当てを解除する必要があります。
_delete [] x;
_
この場合、ポインターx
のメモリは動的に割り当てられ、動的に割り当て解除されるため、そのようなオブジェクトは動的ストレージ期間を持つと言われます。
宣言はまったく異なります。
最初のケース、
_int x[10];
_
x
を_10
_整数の配列として宣言しますが、2番目の場合、
_int* x = new int[10];
_
x
をint
へのポインターとして宣言します-値がint
のアドレスに等しい変数であり、新しい式の結果へのポインターを初期化します(_new int [10]
_) 10個の整数の配列を動的に割り当てます。
違いにかかわらず、2つは同様の方法でusedにすることができます。
x[i]
_、ここでi
は_0
_と_9
_の間の整数値です)は、上記の構文でそれぞれの配列の値を設定または取得するために使用できます;x + i
_は_&x[i]
_と_0
_の間のi
の_10
_と同等です。 [はい、「終わりを過ぎた」アドレスを取得することが可能です];*(x+i)
と_x[i]
_は同等です。i
の場合、_0
_と_9
_の間[「one end the end」ポインターを参照解除すると未定義の動作が得られます]。ただし、たとえば、いくつかの重要な違いもあります。
sizeof
演算子の結果。 sizeof(x)
は、2つの場合に異なる値を与えます。
sizeof(x) == sizeof(int)*10
。 sizeof(int)
は実装定義の値を提供しますが、sizeof(x)/sizeof(*x)
は常に配列内の要素数を提供します(つまり、値_std::size_t
_の_10
_)。sizeof(x) == sizeof(int *)
-実装定義の値です。 sizeof(x)/sizeof(*x)
の値は、_10
_の値を生成することはほとんどありません。つまり、この手法を使用して要素数を取得することはできません。ライフタイム。
最初の場合、x
の有効期間は、宣言が発生するスコープに依存します。宣言がファイルスコープ(つまり、コンパイルブロック内、関数ブロック外)で発生する場合、x
には静的な保存期間があります(プログラムが実行されている限り存在します)。宣言がブロック内で発生した場合、x
-およびそのすべての要素-は、ブロックが終了したときに存在しなくなります。例えば
_{
int x[10];
} // x and all its elements cease to exist here
_
2番目のケースでは、スコープに依存するライフタイムを持つのはポインターx
だけです。動的に割り当てられたメモリ(_new x[10]
_の結果)は割り当て解除されません。これは、x
の有効期間と、それが参照する(動的に割り当てられた)配列の有効期間が分離されることを意味します。
割り当ての結果配列を再割り当てすることはできません。ポインターは(適切にconst
修飾されていない限り)再割り当てできません。
のコンテキストを考えます
_ // x as previously defined in one or the other form
int y[10];
int z;
x = y;
x = &z;
_
最初のケースでは、両方の割り当てによりコンパイラの診断が行われます-割り当ては無効です。 2番目の場合、割り当ては有効であり、x
が(_の最初の要素)y
のアドレスとz
のアドレスをそれぞれ指すようにします。 x
の値が再割り当ての前に別のポインターに格納されていない限り、新しい式(_new int [10]
_)によって割り当てられたメモリがリークされます。プログラムからアクセスできなくなりますが、解放されません。
配列のサイズを動的に変更したい場合、例えば:
void doSomething(int size)
{
int x[size]; // does not compile
int *z = new int[size];
//Do something interesting ...
doMore(z, size);
}
xはC++でコンパイルされないため、zを使用する必要があります。良いニュースは、ほとんどの場合、静的に割り当てられているかのようにzを使用できるようになったことです。
void doMore(int anArray[], int size)
{
// ...
}
zを引数として受け取り、ポインターを配列として扱います。
両方のxが10個の整数の配列の最初のメモリアドレスを指す限り同じですが、その点で非常に異なります
int x[10]
静的ランダムアクセスメモリでメモリを宣言し、キーワード「new」でヒープを使用してメモリを動的に作成します。これは、cでmallocを使用して動的に配列を作成するのとほぼ同じです。
それだけでなく、(理論をテストしていないと思う)次の可能性があります:
int* x = new int[10];
失敗する可能性があり、コンパイラーによっては、エラーまたはNULLポインターを返す可能性があります。 c ++コンパイラがANSI/ISO標準に準拠している場合、例外をスローするのではなく、割り当てが失敗した場合にnullを返す「no-throw」形式のnewをサポートします。
もう1つの違いは、「新しい」演算子をオーバーロードできることです。
ただし、どちらかが(c ++で)nullで終了する配列を作成するかどうかはわかりません。 cで、少なくとも使用するコンパイラでは、境界を過度に拡張することなくそれらを反復処理できると予想される場合は、必ず文字列または配列に\ 0を追加する必要があります。
ちょうど私の$ .02の価値。 :)
最初のケース:x
は、それが非静的ローカル変数であるか静的/グローバル変数であるかに基づいて、スタック/データセグメント上に作成されます。また、x
のアドレスは変更できません。
2番目のケース: 'x'は、ヒープ(フリーストア)で作成された配列を指すポインターです一般的に。他の何かを指すx
も変更できます。さらに、delete[] x;
を使用して割り当てを解除する必要があります