C++の配列に最大長はありますか?
それはC++の制限ですか、それとも私のマシンに依存しますか?調整可能ですか?配列の作成タイプに依存しますか?
どういうわけかその制限を破ることができますか、または情報を保存するより良い方法を検索する必要がありますか?そして、最も簡単な方法は何ですか?
私がしなければならないことは、配列にlong long intを保存することです。私はLinux環境で作業しています。私の質問は次のとおりです。N> 10桁のN個のlong long整数の配列を格納する必要がある場合はどうすればよいですか。
これが必要なのは、学校用の暗号化アルゴリズム(p-Pollardなど)を作成しており、整数の壁と配列表現の長さの壁にぶつかるからです。
2つの制限があり、どちらもC++ではなくハードウェアによって強制されます。
最初の制限(決して到達することはできません)は、配列内のインデックス(およびそのサイズ)を記述するために使用されるサイズタイプの制限によって設定されます。システムのstd::size_t
が取りうる最大値によって与えられます。このデータ型は常にシステムの最大整数型でなければなりません。
もう1つの制限は、物理メモリの制限です。配列内のオブジェクトが大きいほど、メモリがいっぱいであるため、この制限に早く到達します。たとえば、特定のサイズのvector<int>
nは、通常、タイプvector<char>
の配列の約4倍のメモリ(小さな定数値を引いたもの)を使用します。したがって、vector<char>
には、メモリがいっぱいになる前にvector<int>
よりも多くの項目が含まれる場合があります。ネイティブCスタイルの配列int[]
およびchar[]
についても同じカウントです。
また、allocator
はメモリを自由に管理できるため、この上限はvector
の構築に使用されるallocator
のタイプの影響を受ける場合があります。非常に奇妙ですが、それでも考えられないアロケーターは、オブジェクトの同一インスタンスがリソースを共有するような方法でメモリをプールできます。この方法では、多くの同一のオブジェクトをコンテナに挿入できます。そうしないと、利用可能なメモリをすべて使い果たしてしまいます。
それとは別に、C++は制限を強制しません。
誰もサイズの制限について言及していません スタックフレーム。
メモリを割り当てることができる場所は2つあります。
したがって、配列を動的に割り当てる場合(制限は大きく、他の投稿で詳細に説明されます。
int* a1 = new int[SIZE]; // SIZE limited only by OS/Hardware
または、配列がスタックに割り当てられている場合、スタックフレームのサイズによって制限されます。 N.B. ベクトルやその他のコンテナはスタック内にわずかに存在しますが、通常、データの大部分はヒープ上にあります。
int a2[SIZE]; // SIZE limited by COMPILER to the size of the stack frame
理論的な観点ではなく実用的な観点から見ると、32ビットWindowsシステムでは、1つのプロセスで利用可能なメモリの最大合計量は2 GBです。より多くの物理メモリを備えた64ビットオペレーティングシステムに移行することで制限を破ることができますが、これを行うか、代替を探すかは、対象ユーザーとその予算に大きく依存します。 PAE を使用して拡張することもできます。
多くのコンパイラのデフォルトの構造アライメントは8バイトであるため、配列のタイプは非常に重要です。これは、メモリ使用量が問題になる場合は非常に無駄です。 Visual C++を使用してWindowsをターゲットにしている場合、これを克服する方法として #pragma pack ディレクティブを確認してください。
もう1つのことは、スパース行列、オンザフライ圧縮など、メモリ圧縮技術で何が役立つかを調べることです。これもアプリケーションに大きく依存します。投稿を編集して、実際に配列に含まれているものについてさらに情報を提供すると、より有用な回答が得られる場合があります。
編集:正確な要件に関するもう少しの情報を考えると、ストレージのニーズは7.6 GBから76 GBの非圧縮であるように見えます。これには、C++のメモリに配列として保存するためにかなり高価な64ビットボックスが必要になります。アクセス速度を想定し、ランダムアクセスを許可するメモリにデータを保存する理由を疑問視する。このデータを配列の外部に保存する最良の方法は、アクセス方法にほぼ基づいています。配列メンバーにランダムにアクセスする必要がある場合、ほとんどのアプリケーションでは、同時にアクセスされる傾向があるデータの塊をグループ化する方法があります。たとえば、大規模なGISおよび空間データベースでは、データは多くの場合、地理的エリアごとに並べられます。 C++プログラミング用語では、[]配列演算子をオーバーライドして、必要に応じて外部ストレージからデータの一部をフェッチできます。
回答を要約して拡張し、質問に直接回答するには:
いいえ、C++は配列のdimensionsに制限を課しません。
しかし、アレイはメモリのどこかに保存する必要があるため、コンピュータシステムの他の部分によって課されるメモリ関連の制限が適用されます。これらの制限は、配列のdimensions(=要素の数)に直接関係するのではなく、むしろsize(=使用されるメモリの量)に関係することに注意してください。配列の次元(D)とメモリ内サイズ(S)は同じではありません。単一の要素( E):S= D * E。
Now Eは以下に依存します:
また、スタック(自動変数:int t[N]
)またはヒープ(malloc()
/new
を使用した動的な割り当て、またはSTLメカニズムの使用)、またはプロセスの静的な部分に配列データを割り当てることにより、通常、さまざまなメモリ関連の制限が発生することに注意してくださいメモリ(静的変数:static int t[N]
)。ヒープに割り当てる場合でも、ヒープに割り当てられたメモリブロックへの参照を格納するために、スタック上に少しのメモリが必要です(ただし、これは通常無視できます)。size_t
型のサイズはプログラマーに影響を与えません(プログラマーはインデックス用にsize_t
型を使用することを想定しています)。コンパイラプロバイダーは、typedef
を最大に対応できる十分な整数型にする必要があるためです。特定のプラットフォームアーキテクチャで使用可能なメモリ量。
メモリサイズの制限の原因は
アプリケーションレベルで「微調整」することはできませんが、別のコンパイラを使用したり(スタックサイズの制限を変更したり)、アプリケーションを64ビットに移植したり、別のOSに移植したり、物理/ (仮想?物理?)マシンの仮想メモリ構成。
上記のすべての要因を外部障害として、したがって実行時エラーの原因として扱い、プログラムコード内のメモリ割り当て関連エラーを慎重にチェックして対応することは珍しくありません(推奨されません)。
最後に:C++は制限を課していませんが、コードを実行する際にメモリに関連する不利な条件をチェックする必要があります...:-)
上記に同意します。配列を初期化する場合
int myArray[SIZE]
sIZEは整数のサイズによって制限されます。ただし、mallocがNULLを返さない限り、メモリのチャンクをいつでもmallocしてポインタを保持できます。
多くの優れた回答が指摘したように、C++コンパイラのバージョン、オペレーティングシステム、およびコンピューターの特性に依存する多くの制限があります。ただし、Pythonには、マシンの制限をチェックする次のスクリプトをお勧めします。
バイナリ検索を使用し、各反復で、サイズの配列を作成しようとするコードを作成して中間サイズが可能かどうかをチェックします。スクリプトはそれをコンパイルし(申し訳ありませんが、この部分はLinuxでのみ動作します)、成功に応じてバイナリ検索を調整しようとします。見てみな:
import os
cpp_source = 'int a[{}]; int main() {{ return 0; }}'
def check_if_array_size_compiles(size):
# Write to file 1.cpp
f = open(name='1.cpp', mode='w')
f.write(cpp_source.format(m))
f.close()
# Attempt to compile
os.system('g++ 1.cpp 2> errors')
# Read the errors files
errors = open('errors', 'r').read()
# Return if there is no errors
return len(errors) == 0
# Make a binary search. Try to create array with size m and
# adjust the r and l border depending on wheather we succeeded
# or not
l = 0
r = 10 ** 50
while r - l > 1:
m = (r + l) // 2
if check_if_array_size_compiles(m):
l = m
else:
r = m
answer = l + check_if_array_size_compiles(r)
print '{} is the maximum avaliable length'.format(answer)
マシンに保存して起動すると、作成可能な最大サイズが印刷されます。私のマシンでは2305843009213693951です。
前の回答で言及されていないと思うことの1つ。
人々がデザインでそのようなものを使用しているとき、私は常にリファクタリングの意味で「悪臭」を感じています。
これは巨大な配列であり、効率の観点とパフォーマンスの観点の両方からデータを表現する最良の方法ではない可能性があります。
乾杯、
ロブ
大きなデータを処理する必要がある場合は、管理可能なチャンクに分割する必要があります。すべての小型コンピューターのメモリに収まるわけではありません。ディスクのデータの一部(合理的に収まる範囲)をロードし、計算と変更を実行してディスクに保存し、完了するまで繰り返します。
現在のすべての答えがそうであるように、迷惑なほど非特異的であるため、それらはほとんど正しいですが、多くの警告があり、常に言及されているわけではありません。要点は、2つの上限があり、そのうちの1つだけが実際に定義されているものなので、 YMMV :
基本的に、コンパイラが許可するもの。 x64 Windows 10ボックス上のVisual C++ 2017の場合、これは2GBの制限が発生する前のコンパイル時の最大制限です。
unsigned __int64 max_ints[255999996]{0};
代わりにこれを行った場合、
unsigned __int64 max_ints[255999997]{0};
私が得るだろう:
Error C1126 automatic allocation exceeds 2G
2Gが255999996
/7
とどのように相関するかはわかりません。私は両方の数字をグーグルで検索しましたが、関連する可能性のあるものは、 dc
の精度の問題 に関するこの* nix Q&Aだけでした。いずれにせよ、どのタイプのint配列を埋めようとしているかは問題ではなく、割り当て可能な要素の数だけです。
スタックとヒープには独自の制限があります。これらの制限は、利用可能なシステムリソースに基づいて変化する値と、アプリ自体の「重さ」の両方の値です。たとえば、現在のシステムリソースでは、これを実行できます。
int main()
{
int max_ints[257400]{ 0 };
return 0;
}
しかし、少し微調整すれば...
int main()
{
int max_ints[257500]{ 0 };
return 0;
}
バム!スタックオーバーフロー!
Exception thrown at 0x00007FF7DC6B1B38 in memchk.exe: 0xC00000FD:
Stack overflow (parameters: 0x0000000000000001, 0x000000AA8DE03000).
Unhandled exception at 0x00007FF7DC6B1B38 in memchk.exe: 0xC00000FD:
Stack overflow (parameters: 0x0000000000000001, 0x000000AA8DE03000).
そして、アプリポイントの全体の重みを詳細に説明するために、これは良かったです。
int main()
{
int maxish_ints[257000]{ 0 };
int more_ints[400]{ 0 };
return 0;
}
しかし、これによりスタックオーバーフローが発生しました。
int main()
{
int maxish_ints[257000]{ 0 };
int more_ints[500]{ 0 };
return 0;
}
2D動的配列を作成することでこれを回避します。
long long** a = new long long*[x];
for (unsigned i = 0; i < x; i++) a[i] = new long long[y];
すでに指摘したように、アレイのサイズはハードウェアとOSによって制限されます(man ulimit)。しかし、あなたのソフトウェアはあなたの創造性によってのみ制限されるかもしれません。たとえば、「配列」をディスクに保存できますか?本当に長い長い整数が必要ですか?本当に密な配列が必要ですか?配列さえ必要ですか?
1つの簡単な解決策は、64ビットLinuxを使用することです。アレイに物理的に十分なRAMがない場合でも、プロセスで使用可能な仮想メモリは物理メモリよりもはるかに大きいため、OSはメモリを割り当てることができます。本当にアレイ内のすべてにアクセスする必要がある場合、ディスクに保存することになります。アクセスパターンに応じて、これを実行するより効率的な方法があります(つまり、mmap()を使用するか、ファイルにデータを順番に格納するだけです(この場合、32ビットLinuxで十分です)。