web-dev-qa-db-ja.com

プログラムでC ++配列のサイズを決定しますか?

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

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

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

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

これにより、「Size of arr: 4 "、これは単なるポインタのサイズです。256を出力する関数があればいいのですが、C++には存在しないと思います。 )

Clarification:ヒープの代わりにスタックで配列を宣言した場合、つまり「int arr[256]; ")sizeof演算子は1024(配列の長さ* sizeof(int))を返します。

61
Kip

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

65
Dima

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

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

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

18
James Curran

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

実行する場合:int * arr = new int [256];

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

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

int * p_iToSize = arr-4;

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

すべてのmalloc、new、および受信する連続メモリブロックの前には、与えられたメモリブロックに関する情報で予約されたスペースも割り当てられます。

16
João Augusto

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

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);
}
5
Doug T.

いくつかの魔法:

template <typename T, size_t S>
inline
size_t array_size(const T (&v)[S]) 
{ 
    return S; 
}

そして、これがC++ 11での方法です。

template<typename T, size_t S>
constexpr 
auto array_size(const T (&)[S]) -> size_t
{ 
    return S; 
}
3
Zingam

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

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

その後、センチネル値の配列を処理する(または線形検索を行う)ことができます。センチネル値に達すると、配列の数が決まります。

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

3
bearvarine

C++は、typesafe mallocを実行するためにnewを追加することを決定しました。newはctorを呼び出すための要素のサイズとeのサイズの両方を知っている必要があるため、dtorを呼び出すために削除します。初期には、新しいオブジェクトに渡したオブジェクトの番号を削除するには、実際にパスする必要があります。

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

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

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

たぶんそのようなサイズを取得することは可能です:

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

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

3
Mykelyk

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

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

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

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

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

2
Mr Fooz

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

QuantumPete

2
QuantumPete

基本的にできません:

_void foo(int* arr);

int arr[100] = {0};

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

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

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

1
MSalters

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

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

1
Tamas Czinege

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

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

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

1
James Rose

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

1
Greg Rogers

現在、 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()など、他のいくつかの便利な機能も利用できます。

1
Brent Faust

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

  1. いいえ、自分で追跡しない限り。
  2. コンパイラが、その情報について自分自身以外に誰かに伝える必要がない場合、コンパイラの制約が少なくなるためです。それが望ましいかどうかは議論の余地があります。
0
MSN

@Dima、

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

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

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

0
MSN

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

char *ar = new char[100] 

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

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

0
SMeyers

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

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

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

配列の本体は次のようなものです。

int count;
ObjectType* data; //This value is returned when using new[]
0
MessyCode

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

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

100枚印刷します

0
user4443767