C ++に配列の最大長の制限はありますか?


183

C ++の配列に最大長はありますか?

C ++の制限ですか、それとも私のマシンに依存しますか?調整可能ですか?配列の種類に依存しますか?

どういうわけかその制限を破ることはできますか、それとも情報を格納するより良い方法を探す必要がありますか?そして、最も簡単な方法は何でしょうか?

私がしなければならないのは、アレイにlong long intを格納することです。私はLinux環境で作業しています。私の質問は、N> 10桁のN long long整数の配列を格納する必要がある場合はどうすればよいですか?

学校用の暗号化アルゴリズム(p-Pollardなど)を作成していて、整数と配列表現の長さのこの壁にぶつかったため、これが必要です。

回答:


163

2つの制限があり、どちらもC ++ではなくハードウェアによって強制されます。

最初の制限(決して到達してはならない)は、配列内のインデックスを記述するために使用されるサイズタイプ(およびそのサイズ)の制限によって設定されます。これは、システムstd::size_tが取ることができる最大値によって与えられます。このデータ型は、オブジェクトのバイト単位のサイズを含めるのに十分な大きさです

もう1つの制限は、物理メモリの制限です。配列内のオブジェクトが大きいほど、メモリがいっぱいになるため、この制限に早く到達します。たとえばvector<int>、特定のサイズnのvector<char> a intは、通常、よりも大きいため、通常、タイプの配列の数倍のメモリ(小さな定数値を差し引いたもの)を消費しますchar。したがって、メモリがいっぱいになる前にvector<char>、aにvector<int>以前より多くのアイテムが含まれる場合があります。int[]やのような生のCスタイルの配列でも同じことが言えますchar[]

さらに、はメモリを自由に管理できるため、この上限はのallocator作成に使用されるタイプの影響を受ける可能性があります。非常に奇妙ですがそれでも考えられるアロケータは、オブジェクトの同一のインスタンスがリソースを共有するような方法でメモリをプールできます。このようにして、使用可能なすべてのメモリを使い果たしてしまう多数の同一のオブジェクトをコンテナに挿入できます。vectorallocator

それとは別に、C ++は制限を強制しません。


20
また、特に実装固有のスレッドを使用している場合(ただし、変更可能)は、通常、スタックサイズの制限に簡単に達する可能性があります。
アラリック

@アラリック:そうだ。システムの詳細にはあまり深く入りたくありませんでした。それらは非常に異なっており、私はそれらの専門家ではないからです。
Konrad Rudolph、

@Konrad、アロケーターのタイプに関する興味深い点であり、私が気付いていなかったもの。情報をありがとう。
SmacL 2008年

11
std :: size_tは通常(常に?)ポインターのサイズであり、整数演算ユニットでネイティブハードウェアがサポートする最大の整数のサイズではありません。私が使用したすべてのx86 OSで、size_tは32ビットOSの場合は32ビット、64ビットOSの場合は64ビットです。
Fooz氏、2009

2
私の理解では、配列の最大制限はプロセッサのワードの最大値であるということです。これは、インデックス付け演算子が原因です。たとえば、マシンのワードサイズは16ビットですが、アドレッシングレジスタは32ビットです。メモリのチャンクは、newまたはに渡されるパラメータによってサイズが制限されますmalloc。配列よりも大きいメモリのチャンクは、ポインタを介してアクセスできます。
Thomas Matthews

171

スタックフレームのサイズの制限については誰も触れていません。

メモリを割り当てることができる場所は2つあります。

  • ヒープ(動的に割り当てられたメモリ)。
    ここでのサイズ制限は、使用可能なハードウェアと、他のデバイスを使用して未使用のデータを一時的に保存する(つまり、ページをハードディスクに移動する)ことによって領域をシミュレートするOSの機能の組み合わせです。
  • スタック上(ローカルに宣言された変数)。
    ここでのサイズ制限はコンパイラー定義です(可能なハードウェア制限を使用)。コンパイラのドキュメントを読んだ場合、このサイズを微調整できることがよくあります。

したがって、配列を動的に割り当てる場合(制限は大きく、他の投稿で詳細に説明されています)。

int* a1 = new int[SIZE];  // SIZE limited only by OS/Hardware

または、配列がスタックに割り当てられている場合、スタックフレームのサイズによって制限されます。NBベクターと他のコンテナーはスタック内にわずかしか存在しませんが、通常、データの大部分はヒープにあります。

int a2[SIZE]; // SIZE limited by COMPILER to the size of the stack frame

4
大きな配列の優先割り当ては、スタック上またはグローバルに定義されたものではなく、動的割り当て(newまたはmalloc)を介して行われます。
Thomas Matthews

1
@トーマスマシューズ:私の世界ではありません。動的に割り当てられたオブジェクトには管理が必要です。動的に割り当てる必要がある場合は、std :: vectorのように、動的に割り当てられたメモリを表すスタックオブジェクトを使用します。
マーティンヨーク

2
1 cornorケース欠けがあります:Global Arrays、避け美しさと最高ではないが、これらはの制限に該当しないstack、とあなたは必要としないmalloc/ free彼らと仕事をします。
2013

1
@ted、なぜグローバル配列は「最善の回避」が必要なのでしょうか?より正確には、静的に割り当てられた配列を意味していると思います。それらのスコープはグローバルである必要はありません。動的に割り当てられた配列では実行できない絶対アドレス指定を(少なくともLinuxでは)使用できるため、動的配列よりも優れていると私は主張します。
Zボソン2014年

2
非常に重要なポイント。私は最近、構成可能な最大バッファーサイズを提供する「本番品質」のオープンソースプロジェクトに出会いました。すべてのバッファがスタックに割り当てられたため、十分に大きな値を構成すると、プログラムは起動時にすぐにsegfaultします。
2014

13

理論的ではなく実用的な観点から見た場合、32ビットのWindowsシステムでは、単一のプロセスで使用可能なメモリの最大総量は2 GBです。物理メモリがはるかに多い64ビットオペレーティングシステムに移行することで制限を破ることができますが、これを実行するか、代替手段を探すかは、対象となるユーザーとその予算に大きく依存します。PAEを使用して多少拡張することもできます。

多くのコンパイラーのデフォルトの構造調整は8バイトであるため、配列のタイプは非常に重要です。これは、メモリーの使用が問題になる場合は非常に無駄です。Visual C ++を使用してWindowsをターゲットにしている場合は、これを克服する方法として#pragma packディレクティブを確認してください。

もう1つのことは、スパースマトリックスやオンザフライ圧縮など、メモリ内の圧縮技術が役立つ可能性があることを確認することです。これもアプリケーションに大きく依存します。投稿を編集して、実際に配列に何があるかについてさらに情報を提供すると、より役立つ回答が得られる場合があります。

編集:正確な要件に関するもう少し詳しい情報を考えると、ストレージのニーズは非圧縮で7.6 GBから76 GBの間であるように見え、C ++のメモリに配列として格納するにはかなり高価な64ビットボックスが必要になります。それはなぜデータをメモリに格納したいのかという疑問を提起します。ここでは、アクセスの速度を推定し、ランダムアクセスを許可します。このデータを配列の外に格納する最良の方法は、ほとんどどのようにアクセスしたいかに基づいています。アレイメンバーにランダムにアクセスする必要がある場合、ほとんどのアプリケーションでは、同時にアクセスされる傾向があるデータのまとまりをグループ化する方法が存在する傾向があります。たとえば、大規模なGISおよび空間データベースでは、データが地理的領域ごとにタイル化されることがよくあります。C ++プログラミング用語では、[]配列演算子をオーバーライドして、必要に応じて外部ストレージからデータの一部をフェッチできます。


1
プログラム空間外のメモリの割り当てを可能にするシステムコールがあります。ただし、これはOSに依存し、移植性はありません。組み込みシステムで使用しました。
Thomas Matthews

4

私は上記に同意します、あなたがあなたの配列を初期化しているなら

 int myArray[SIZE] 

その場合、SIZEは整数のサイズによって制限されます。ただし、mallocがNULLを返さない限り、いつでもメモリのチャンクをmallocして、そのポインタを必要なだけ大きくできます。


これが間違っているのか、あなたや他の何かを誤解しているのかはわかりません。たとえば、これはMSVC17コンパイラーによって防止されます:int oops[INT_MAX]{0};生成されますC2148 - total size of array must not exceed 0x7fffffff bytes
kayleeFrye_onDeck

16GB DDR4と66%、VS2017を搭載したWindows 10でデバッグとしてアプリを起動する前に現在使用されているメモリについて、私はで初期化できるint-arrayの大きさに未定義の制限があります0。時々私は〜257kの要素でそれを行うことができ、時々私はスタックオーバーフローを取得します。メインと配列以外に何かをアプリに追加すると、その数は(明らかに)減少します。私はこの数を決定するために実験をしなければならなかったので、真空での理論上の限界を知る以外に、このメトリックがどのように信頼できるかわかりません。
kayleeFrye_onDeck 2018

4

応答を要約し、それらを拡張して、質問に直接回答するには:

いいえ、C ++は配列の次元に制限を課しません。

ただし、アレイはメモリのどこかに保存する必要があるため、コンピュータシステムの他の部分によって課されるメモリ関連の制限が適用されます。これらの制限は、配列の次元(=要素の数)に直接関係しているのではなく、そのサイズ(=取得したメモリの量)に関係していることに注意してください。寸法(D)と、メモリ内サイズ(S、それらは単一の要素(で撮影されたメモリに関連しているように、アレイの)は、同じではありませんE):S = D * E

Eは以下に依存します:

  • 配列要素のタイプ(要素は小さくても大きくてもかまいません)
  • メモリの位置合わせ(パフォーマンスを向上させるために、要素はいくつかの値の乗算であるアドレスに配置されます。これ
    により、要素間に「無駄なスペース」(パディング)が発生します。
  • オブジェクトの静的部分のサイズ(オブジェクト指向プログラミングでは、同じタイプのオブジェクトの静的コンポーネントは、そのような同じタイプのオブジェクトの数に関係なく、一度だけ保存されます)

また、配列データをスタック(自動変数としてint t[N])、またはヒープ(malloc()/ newまたはSTLメカニズムを使用した動的割り当て)、またはプロセスメモリの静的部分(静的変数:)static int t[N]。ヒープに割り当てる場合でも、ヒープに割り当てられたメモリブロックへの参照を格納するために、スタック上に少量のメモリが必要です(ただし、通常は無視できます)。型

のサイズはsize_tプログラマーに影響を与えません(プログラマーはsize_t型のために設計されているため、型をインデックス付けに使用すると想定します)。コンパイラープロバイダーはtypedef、指定されたプラットフォームで可能な最大メモリ容量に対応するのに十分な大きさの整数型にする必要があるためです。建築。

メモリサイズの制限の原因は、

  • プロセスで使用可能なメモリの量(32ビットアプリケーションの場合、64ビットOSカーネルでも2 ^ 32バイトに制限されます)、
  • プロセスメモリの分割(たとえば、スタックまたはヒープ用に設計されたプロセスメモリの量)
  • 物理メモリの断片化(多くの散在する小さな空きメモリフラグメントは、1つのモノリシック構造の格納には適用されません)、
  • 物理メモリの量、
  • 仮想メモリの量。

これらはアプリケーションレベルで「調整」することはできませんが、別のコンパイラを使用して(スタックサイズの制限を変更)、アプリケーションを64ビットに移植したり、別のOSに移植したり、物理/ (仮想?物理?)マシンの仮想メモリ構成。

上記のすべての要因を外乱として、したがって実行時エラーの考えられる原因として扱い、プログラムコード内のメモリ割り当て関連のエラーを注意深くチェックして対処することは、珍しいことではありません。

だから最後に:C ++は制限を課しませんが、コードを実行するときに、メモリ関連の不利な条件をチェックする必要があります... :-)


3

多くの優れた回答が述べたように、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です。


2

以前の回答で私が考えていないことが1つあります。

私は、人々がデザインでそのようなものを使用しているとき、リファクタリングの意味で常に「悪臭」を感じています。

これは巨大な配列であり、効率の観点とパフォーマンスの観点の両方からデータを表現するための最良の方法ではない可能性があります。

乾杯、

ロブ


私が何を使うべきかについて何か提案はありますか?
luiss

保存しているデータを教えていただければ、多分私たちができるでしょう。(-:
Rob Wells、

すいませんルイス私の最初の応答は非常に軽快でした。それはあなたのデータの性質によって駆動されます。データの関係性は、データを表すために使用するモデルを駆動します。それからコレクションそれから明らかであるはずです。そうでなければ、私はデータモデルについて心配するでしょう。
Rob Wells、

私にはそれほどひどくはありません:このようなおもちゃでキャッシュされたデータベースはどうですか? tweaktown.com/news/22066/...

2

このように大きなデータを処理する必要がある場合は、扱いやすいチャンクに分割する必要があります。すべての小型コンピュータのメモリに収まるわけではありません。おそらく、ディスクからデータの一部をロードし(合理的に適合するものは何でも)、計算とデータへの変更を実行し、ディスクに格納し、完了するまで繰り返します。


大きすぎるデータを処理してメモリに収まらないアルゴリズムの例については、マージソートも参照してください。
Thomas Matthews

2

現在のすべての回答がそうであるように、いらいらするほど具体的ではありませんが、それらはほとんど正しいですが、常に言及されているわけではない多くの警告があります。要点は、2つの上限があり、そのうちの1つだけが実際に定義されたものであるため、YMMVです。

1.コンパイル時間の制限

基本的に、コンパイラが許可するもの。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。私は両方の数字をグーグルで検索しましたが、関連している可能性があるのは、この* nixに関する精度に関する問題dcに関するQ&Aだけでした。どちらの方法でも、どのタイプのint配列に入力しようとしているかは問題ではないようです。割り当て可能な要素の数だけです。

2.実行時の制限

スタックとヒープには独自の制限があります。これらの制限は両方とも、利用可能なシステムリソースに基づいて変化する値であり、アプリ自体の「重い」ものです。たとえば、現在のシステムリソースを使用して、これを実行できます。

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;
}  

1

std :: vectormax_size()メンバー関数がここで言及されていないことに驚いています。

「システムまたはライブラリの実装制限により、コンテナが保持できる要素の最大数を返します。つまり、最大のコンテナの場合はstd :: distance(begin()、end())です。」

std::vectorこれmax_size()は、内部で動的配列として実装されていることがわかっているため、マシンの動的配列の最大長の非常に近い概算が得られるはずです。

次のプログラムは、さまざまなデータ型の配列の最大長のおおよその表を作成します。

#include <iostream>
#include <vector>
#include <string>
#include <limits>

template <typename T>
std::string mx(T e) {
    std::vector<T> v;
    return std::to_string(v.max_size());
}

std::size_t maxColWidth(std::vector<std::string> v) {
    std::size_t maxWidth = 0;

    for (const auto &s: v)
        if (s.length() > maxWidth)
            maxWidth = s.length();

    // Add 2 for space on each side
    return maxWidth + 2;
}

constexpr long double maxStdSize_t = std::numeric_limits<std::size_t>::max();

// cs stands for compared to std::size_t
template <typename T>
std::string cs(T e) {
    std::vector<T> v;
    long double maxSize = v.max_size();
    long double quotient = maxStdSize_t / maxSize;
    return std::to_string(quotient);
}

int main() {
    bool v0 = 0;
    char v1 = 0;

    int8_t v2 = 0;
    int16_t v3 = 0;
    int32_t v4 = 0;
    int64_t v5 = 0;

    uint8_t v6 = 0;
    uint16_t v7 = 0;
    uint32_t v8 = 0;
    uint64_t v9 = 0;

    std::size_t v10 = 0;
    double v11 = 0;
    long double v12 = 0;

    std::vector<std::string> types = {"data types", "bool", "char", "int8_t", "int16_t",
                                      "int32_t", "int64_t", "uint8_t", "uint16_t",
                                      "uint32_t", "uint64_t", "size_t", "double",
                                      "long double"};

    std::vector<std::string> sizes = {"approx max array length", mx(v0), mx(v1), mx(v2),
                                      mx(v3), mx(v4), mx(v5), mx(v6), mx(v7), mx(v8),
                                      mx(v9), mx(v10), mx(v11), mx(v12)};

    std::vector<std::string> quotients = {"max std::size_t / max array size", cs(v0),
                                          cs(v1), cs(v2), cs(v3), cs(v4), cs(v5), cs(v6),
                                          cs(v7), cs(v8), cs(v9), cs(v10), cs(v11), cs(v12)};

    std::size_t max1 = maxColWidth(types);
    std::size_t max2 = maxColWidth(sizes);
    std::size_t max3 = maxColWidth(quotients);

    for (std::size_t i = 0; i < types.size(); ++i) {
        while (types[i].length() < (max1 - 1)) {
            types[i] = " " + types[i];
        }

        types[i] += " ";

        for  (int j = 0; sizes[i].length() < max2; ++j)
            sizes[i] = (j % 2 == 0) ? " " + sizes[i] : sizes[i] + " ";

        for  (int j = 0; quotients[i].length() < max3; ++j)
            quotients[i] = (j % 2 == 0) ? " " + quotients[i] : quotients[i] + " ";

        std::cout << "|" << types[i] << "|" << sizes[i] << "|" << quotients[i] << "|\n";
    }

    std::cout << std::endl;

    std::cout << "N.B. max std::size_t is: " <<
        std::numeric_limits<std::size_t>::max() << std::endl;

    return 0;
}

私のmacOS(clangバージョン5.0.1)では、次のようになります。

|  data types | approx max array length | max std::size_t / max array size |
|        bool |   9223372036854775807   |             2.000000             |
|        char |   9223372036854775807   |             2.000000             |
|      int8_t |   9223372036854775807   |             2.000000             |
|     int16_t |   9223372036854775807   |             2.000000             |
|     int32_t |   4611686018427387903   |             4.000000             |
|     int64_t |   2305843009213693951   |             8.000000             |
|     uint8_t |   9223372036854775807   |             2.000000             |
|    uint16_t |   9223372036854775807   |             2.000000             |
|    uint32_t |   4611686018427387903   |             4.000000             |
|    uint64_t |   2305843009213693951   |             8.000000             |
|      size_t |   2305843009213693951   |             8.000000             |
|      double |   2305843009213693951   |             8.000000             |
| long double |   1152921504606846975   |             16.000000            |

N.B. max std::size_t is: 18446744073709551615

ideoneのGCC 8.3私が取得します:

|  data types | approx max array length | max std::size_t / max array size |
|        bool |   9223372036854775744   |             2.000000             |
|        char |   18446744073709551615  |             1.000000             |
|      int8_t |   18446744073709551615  |             1.000000             |
|     int16_t |   9223372036854775807   |             2.000000             |
|     int32_t |   4611686018427387903   |             4.000000             |
|     int64_t |   2305843009213693951   |             8.000000             |
|     uint8_t |   18446744073709551615  |             1.000000             |
|    uint16_t |   9223372036854775807   |             2.000000             |
|    uint32_t |   4611686018427387903   |             4.000000             |
|    uint64_t |   2305843009213693951   |             8.000000             |
|      size_t |   2305843009213693951   |             8.000000             |
|      double |   2305843009213693951   |             8.000000             |
| long double |   1152921504606846975   |             16.000000            |

N.B. max std::size_t is: 18446744073709551615

これは理論上の制限であり、ほとんどのコンピューターでは、この制限に達する前にメモリが不足することに注意してください。たとえば、タイプcharonのgcc場合、要素の最大数はの最大数に等しいことがわかりstd::size_tます。これを試しみると、エラーが発生します:

prog.cpp: In function int main()’:
prog.cpp:5:61: error: size of array is too large
  char* a1 = new char[std::numeric_limits<std::size_t>::max()];

最後に、@ MartinYorkが指摘するように、静的配列の場合、最大サイズはスタックのサイズによって制限されます。


0

すでに指摘したように、配列サイズはハードウェアとOS(man ulimit)によって制限されます。ただし、ソフトウェアは、創造性によってのみ制限される場合があります。たとえば、「アレイ」をディスクに保存できますか?本当に長い整数が必要ですか?本当に高密度アレイが必要ですか?配列も必要ですか?

簡単な解決策の1つは、64ビットLinuxを使用することです。アレイに十分なRAMがない場合でも、プロセスで使用できる仮想メモリは物理メモリよりもはるかに大きいため、OSはメモリを割り当てることができます。本当にアレイ内のすべてにアクセスする必要がある場合、これはディスクに格納することになります。アクセスパターンによっては、これを行うためのより効率的な方法がある場合があります(つまり、mmap()を使用するか、単純にファイルにデータを順次格納します(この場合、32ビットLinuxで十分です))。


2
うーん、ディスク、アレイ、...誰もが仮想メモリについて聞いています仮想メモリをサポートするOSは、ハードディスクなどのメモリ用の外部デバイスの使用を開始し、内部メモリでチャンクをスワップアウトします。
Thomas Matthews

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.