62

この質問は、同様の質問に触発されました: delete[] はオペランド配列のサイズをどのように「認識」しますか?

私の質問は少し異なります: プログラムで C++ 配列のサイズを決定する方法はありますか? そうでない場合、なぜですか? 配列を取るすべての関数は、サイズを指定するために整数パラメーターも必要とします。しかし、リンクされた質問が指摘したように、delete[]割り当てを解除するメモリのサイズを知っている必要があります。

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

int* arr = new int[256];
printf("Size of arr: %d\n", sizeof(arr));

これSize of arr: 4は、ポインターのサイズである " " を出力します。256 を出力する関数があればいいのですが、C++ には存在しないと思います。(繰り返しますが、問題の一部は、なぜそれが存在しないのかということです。)

明確化int arr[256];: ヒープ (つまり " ") ではなくスタックで配列を宣言すると、sizeof演算子は 1024 (配列の長さ * sizeof(int)) を返すことを知っています。

4

20 に答える 20

70

delete []割り当てられたサイズを知っています。ただし、その知識はランタイムまたはオペレーティング システムのメモリ マネージャーに存在するため、コンパイル中にコンパイラで使用することはできません。そしてsizeof()実際の関数ではなく、実際にはコンパイラによって定数に評価されます。これは、コンパイル時にサイズがわからない動的に割り当てられた配列では実行できないことです。

また、次の例を検討してください。


int *arr = new int[256];
int *p = &arr[100];
printf("Size: %d\n", sizeof(p));

コンパイラは のサイズをどのように知るのでしょうpか? 問題の根源は、C および C++ の配列がファーストクラスのオブジェクトではないことです。それらはポインターに崩壊し、ポインターが によって割り当てられたメモリのチャンクの先頭を指しているのかnew、単一のオブジェクトを指しているのか、チャンクの途中の場所を指しているのかを、コンパイラまたはプログラム自体が知る方法はありません。によって割り当てられたメモリのnew

この理由の 1 つは、C と C++ がメモリ管理をプログラマーとオペレーティング システムに任せていることです。これが、ガベージ コレクションがない理由でもあります。との実装はC++ 標準の一部ではnewありdeleteません。C++ はさまざまなプラットフォームで使用されることを意図しており、メモリを非常に異なる方法で管理する可能性があるためです。最新の Intel CPU で動作する Windows ボックス用のワード プロセッサを作成している場合、C++ にすべての割り当てられた配列とそのサイズを追跡させることは可能かもしれません。 DSP。

于 2008-10-13T15:03:26.007 に答える
20

実際にはサイズを決定する方法がありますが、それは「安全」ではなく、コンパイラごとに異なるため、まったく使用しないでください

その場合: int* arr = new int[256];

256 は無関係です。この場合 1024 を想定して 256*sizeof(int) が与えられます。この値はおそらく ( arr - 4 ) に格納されます。

だからあなたに「アイテム」の数を与えるために

int* p_iToSize = arr - 4;

printf("アイテム数 %d", *p_iToSize / sizeof(int));

すべての malloc、new、受信した連続メモリ ブロックの前に、与えられたメモリ ブロックに関する情報を格納するスペースが割り当てられます。

于 2008-10-13T16:07:26.983 に答える
19

いいえ、標準 C++ でそれを行う方法はありません。

私が知っていることではない、本当に正当な理由はありません。おそらく、サイズは実装の詳細と見なされ、公開しないのが最善です。malloc(1000) と言った場合、返されるブロックが 1000 バイトであるという保証はないことに注意してください ---少なくとも1000 バイトであるということだけです。ほとんどの場合、約 1020 (1K からオーバーヘッドの 4 バイトを差し引いた値) です。その場合、「1020」サイズは、ランタイム ライブラリが覚えておくべき重要なサイズです。そしてもちろん、それは実装間で変化します。

これが、標準委員会が正確なサイズを追跡する std:vector<> を追加した理由です。

于 2008-10-13T14:58:18.280 に答える
5

これを処理する一般的な方法は、ベクトルを使用することです

int main()
{
   std::vector<int> v(256);
   printf("size of v is %i capacity is %i\n", sizeof(int) * v.size(), sizeof(int) * v.capacity());
}

またはサイズを事前に定義する

const int arrSize = 256;
int main()
{
    int array[arrSize];
    printf("Size of array is %i", sizeof(int) * arrSize);
}
于 2008-10-13T14:56:13.570 に答える
4

C++ は、タイプセーフな malloc を実行するために new を追加することを決定しました。これは、new が ctor を呼び出すための要素のサイズと数の両方を知っている必要があるため、dtor を呼び出すために削除します。初期の頃は、new に渡したオブジェクトの番号を実際に削除するために渡さなければなりませんでした。

string* p = new string[5];
delete[5] p;

ただし、new<type>[] を使用すると、数値のオーバーヘッドが小さいと考えられていました。そのため、new[n] は n を記憶し、それを delete に渡す必要があると判断しました。それを実装するには、主に 3 つの方法があります。

  1. サイズへのポインターのハッシュテーブルを保持する
  2. ベクトルのすぐ近くに書いた
  3. まったく違うことをする

おそらくそのようなサイズを取得することは可能です:

size_t* p = new size_t[10];
cout << p[-1] << endl;
// Or
cout << p[11] << endl;

または、それらのどれも地獄。

于 2008-10-13T15:37:13.583 に答える
3

アプリケーションによっては、配列の最後に「センチネル値」を作成できます。

センチネル値には、一意のプロパティが必要です。

その後、配列を処理して (または線形検索を実行して)、センチネル値を数えながら数えることができます。センチネルの値に達すると、配列の数が得られます。

単純な C 文字列の場合、末尾の \0 はセンチネル値の例です。

于 2013-05-15T15:15:22.797 に答える
2

C++ で動的に割り当てられた配列のサイズを決定する移植可能な方法は、そのポインターだけが与えられた場合にはありません。C++ は非常に柔軟で、ユーザーに力を与えるように作られています。たとえば、標準では、必要なサイズのヘッダーを追加するなど、メモリ アロケータがどのように動作する必要があるかを定義していません。ヘッダーを必要としないため、柔軟性が大幅に向上します。

一例として、char * 配列として実装された文字列を考えてみましょう。部分文字列を選択するために、配列の中央へのポインターを使用するのが一般的です。例として、標準 C ライブラリの strtok 関数を参照してください。各配列の直前にヘッダーを埋め込む必要がある場合は、部分文字列の前に配列の一部を破棄する必要があります。

ヘッダーを処理する別の方法は、メモリの 1 つのブロックに配列ヘッダーを配置し、別の場所にある未加工の配列メモリを指すようにすることです。多くの場合、これには参照ごとに 2 つのポインター ルックアップが必要であり、パフォーマンスが大幅に低下します。これらの欠陥を克服する方法はありますが、複雑さが増し、実装の柔軟性が低下します。

std::vector テンプレートは、配列のサイズを配列自体にバインドしておくための私のお気に入りの方法です。

C は、より優れた構文を持つ移植可能なアセンブリ言語です。

于 2008-10-13T15:06:36.903 に答える
2

現在、定数サイズの配列を効率的にコンパイルするためのラッパーであるstd::arrayがあります。

#include <array>

int main (int argc, char** argv)
{
    std::array<int, 256> arr;
    printf("Size of arr: %ld\n", arr.size());
}

パラメータは<type, #elements>.

また、イテレータ、empty()、max_size() など、いくつかの優れた機能も利用できます。

于 2013-12-07T02:12:35.173 に答える
2

これは、変数 arr が単なるポインターであるためです。それについて何も知らなくても、メモリ内の特定の場所のアドレスを保持します。int* として宣言すると、ポインターをインクリメントするときに何をすべきかをコンパイラーに示します。それ以外では、配列の先頭または末尾、スタック、または無効なメモリを指している可能性があります。しかし、私はあなたに同意します.sizeofを呼び出すことができないのは非常に面倒です:)

クォンタムピート

于 2008-10-13T14:56:22.527 に答える
1

基本的に、次のことはできません。

void foo(int* arr);

int arr[100] = {0};

foo(arr+1); // Calls foo with a pointer to 100-1 elements.

C++ 配列は、連続したメモリ領域に格納されているオブジェクトのコレクションにすぎません。それらの間に穴がないため (パディングはオブジェクト内にあります)、ポインタをインクリメントするだけで配列の次の要素を見つけることができます。CPU レベルでは、これは単純な調整です。C++ は sizeof(element) 乗数のみを挿入します。

実装は、配列境界を含む「ファット ポインター」の実装を選択する場合があることに注意してください。ある種の「配列バインド記述子」にリンクする必要があるため、それらは2倍の大きさである必要があります。副作用として、そのような実装で呼び出すことができる可能性がありますdelete [] (1+new int[5]);

于 2008-10-13T15:03:48.097 に答える
1

残念ながら、これは不可能です。C および C++ では、配列の長さはどこにも格納されないため、プログラマは配列の長さを覚えておく必要があります。Delete[] と free() は割り当てられたブロックのサイズを記憶していますが、要求されたよりも多くのメモリを割り当てる可能性があるため、割り当てられたメモリ ブロックのサイズを格納する内部データ構造では、配列の正確なサイズが得られない場合があります。

基本的にいくつかのヘルパー関数を含むクラスにラップされた配列である C++ STL ベクトルは、配列の長さを格納するため、この機能が本当に必要な場合は、ベクトルを使用できます。

于 2008-10-13T15:06:25.313 に答える
1

一般的に、いいえ。C および C++ の配列は、簿記情報が添付されていないメモリの単なるブロックです。配列の長さをメモリに格納し、そのためのオーバーヘッドを追加しないと、一般的なケースでは不可能です。

静的に割り当てられる配列には例外があります。たとえば、次のように宣言すると、int a[50]次のsizeof(a)ように動作します。[50] は配列の静的型の一部であるため、これが可能です。これはコンパイラーに認識されています。sizeof はコンパイル時に解釈されます。

ただし、 pointer: を作成するint *p = asizeof(p)、コンパイラは p が何を指しているのかわからないため、配列のサイズではなく、言及したポインターのサイズを返します。

于 2008-10-13T15:08:00.363 に答える
1

いいえ、これを行う方法はありません。外部からどれだけ大きいかを追跡する必要があります。のようなクラスstd::vectorがこれを行います。

于 2008-10-13T14:57:23.507 に答える
0

プログラムでC++配列のサイズを決定する方法はありますか?そうでない場合は、なぜですか?

  1. いいえ、自分で追跡しない限り。
  2. なぜなら、コンパイラーがそれ自体以外の誰かにその情報について伝える必要がない場合、コンパイラーの制約が少なくなるからです。それが望ましいかどうかは議論の余地があります。
于 2008-10-15T20:40:56.650 に答える
0

@ディマ、

コンパイラはpのサイズをどのようにして知るのでしょうか?

コンパイラはpのサイズを知っている必要があります。それ以外の場合は、を実装できませんdelete[]。コンパイラは、それをどのように理解するかを他の人に伝える必要はありません。

これを確認する楽しい方法として、によって返されたポインタを。によって返されたポインタと比較してoperator new[]くださいnew[]

于 2008-10-15T20:42:53.307 に答える
0

私がそれを行う方法は、配列のサイズを最初の要素のサイズで割ることです

int intarray[100];
printf ("Size of the array %d\n", (sizeof(intarray) / sizeof(intarray[0]));

100を印刷します

于 2015-01-12T02:58:27.277 に答える
0

配列ポインターを作成するとき (ポインターへのテンプレートを使用してラッパーを作成する) はできませんが、オブジェクトの配列を作成するときは、次のように配列のサイズを取得できます。

char* chars=new char[100];
printf("%d",*((int*)chars-1));

関数は、そのdelete[]中のすべてのオブジェクトを分解する必要があります。それを行うには、new[]キーワードは要素の数をすべての配列の後ろに置きます。

配列の本体は次のようになります。

int count;
ObjectType* data; //This value is returned when using new[]
于 2012-09-19T20:32:01.363 に答える
0

コンパイラはそれを知ることができません

char *ar = new char[100] 

メモリ内に実際の配列を作成しないため、メモリ内に初期化されていない 100 バイトへのポインタを作成するだけなので、100 文字の配列です。

指定された配列のサイズを知りたい場合は、std::vector を使用してください。std::vector は単純に優れた配列です。

于 2009-01-07T11:16:05.707 に答える
-1

配列の余分な要素を作成してから、配列に格納される可能性が最も低い数値を適用することができます。次に、その数を渡すことにより、何らかの関数を介して要素の数を決定できます。

作成時に配列を宣言して初期化する場合は、配列をスキャンして、配列のどの要素とも一致しない数値を生成できます。しかし、要素の 1 つを変更すると、その要素が最後の要素と同じ値を格納しているかどうかがわからないため、最後の要素に格納する新しい数値を生成する必要があります。作成時の要素の総数を変数に格納することもできます。関数内で配列のみを使用する場合は、おそらくそうなるでしょう。

于 2012-10-26T15:13:20.257 に答える