大きな配列サイズでのセグメンテーション違反


116

次のコードは、2Gbマシンで実行するとセグメンテーション違反を引き起こしますが、4GBマシンでは動作します。

int main()
{
   int c[1000000];
   cout << "done\n";
   return 0;
}

アレイのサイズはわずか4Mbです。C ++で使用できる配列のサイズに制限はありますか?

回答:


130

ここでスタックオーバーフローが発生している可能性があります。配列が大きすぎて、プログラムのスタックアドレス空間に収まりません。

ヒープに配列を割り当てる場合は、マシンに十分なメモリがあると想定して、問題ないはずです。

int* array = new int[1000000];

ただし、これにdelete[]は配列が必要になることを覚えておいてください。より良い解決策は、それを使用std::vector<int>して1000000要素にサイズ変更することです。


3
回答ありがとうございます。なぜ配列がスタックに割り当てられ、なぜメインプログラムメモリに割り当てられないのか説明してください。
Mayank、2009

18
指定されたコードは、コンパイル時に要素数が一定の配列として指定されているため、スタックに割り当てられます。値のみ、malloc関数でヒープ上に置く新しい、などされている
セス・ジョンソン

6
すべての自動変数はスタックに割り当てられます。逆アセンブルを見ると、ローカル変数のサイズがスタックポインターから差し引かれています。malloc、calloc、またはメモリ機能のいずれかを呼び出すと、機能が実行され、要求を満たすのに十分な大きさのメモリブロックが見つかります。
再実行

@Charlesスタックではなくヒープからより多くのメモリを割り当てることができるのはなぜですか?私の理解では、スタックとヒープの両方がメモリ内の割り当てられたアドレス空間で反対方向に移動します。
saurabh agarwal 2015

2
@saurabhagarwalヒープは移動しません。それは連続したメモリ領域でさえありません。アロケータは、サイズ要件に適合する空きメモリブロックを返すだけです。スタックとヒープはどこにありますか。
phuclv 2015年

56

CまたはC ++では、ローカルオブジェクトは通常スタックに割り当てられます。スタックが処理できる以上の大きな配列をスタックに割り当てているため、stackoverflowが発生しています。

スタック上でローカルに割り当てないでください。代わりに他の場所を使用してください。これは、オブジェクトをグローバルにするか、グローバルヒープに割り当てることによって実現できます。他のコンパイルユニットのを使用しない場合は、グローバル変数で問題ありません。これが誤って発生しないようにするには、静的ストレージ指定子を追加します。そうでない場合は、ヒープを使用します。

これにより、ヒープの一部であるBSSセグメントに割り当てられます。

static int c[1000000];
int main()
{
   cout << "done\n";
   return 0;
}

これにより、ヒープの一部でもあるDATAセグメントに割り当てられます。

int c[1000000] = {};
int main()
{
   cout << "done\n";
   return 0;
}

これにより、ヒープ内の指定されていない場所に割り当てられます。

int main()
{
   int* c = new int[1000000];
   cout << "done\n";
   return 0;
}

ヒープに割り当てる3番目のパターンを使用する場合は、ある時点でポインタを削除することを忘れないでください。そうしないと、メモリがリークします。または、スマートポインターを調べます。
davidA 2012

8
@meowsqueakもちろん、でdelete割り当てる場所はどこでも良い習慣ですnew。しかし、メモリを(mainのように)1回だけ割り当てることが確実である場合、それは厳密には必要ありません-明示的に指定しなくても、mainの出口でメモリが解放されることが保証されますdelete
Gunther Piez 2012

'at'drhirsch(とにかくアットキャラクターをどのように使用しますか?)-はい、公平なコメント。OPは言語に新しいように見えるので、彼らとあなたの良い答えを見ている他の誰もが、一般的に使用される場合の3番目のオプションの影響を認識していることを確認したかっただけです。
davidA 2012

15

また、ほとんどのUNIXおよびLinuxシステムで実行している場合は、次のコマンドで一時的にスタックサイズを増やすことができます。

ulimit -s unlimited

しかし、注意してください。メモリは限られたリソースであり、大きな力には大きな責任があります:)


1
これが解決策ですが、プログラムのスタックサイズに対するこのデフォルトの制限を削除するときは、すべてに非常に注意することをお勧めします。パフォーマンスが大幅に低下するだけでなく、システムがクラッシュする可能性があります。たとえば、4GBのRAMを搭載したマシンで、クイックソートを使用して16 000 000の整数要素を含む配列をソートしようとすると、システムがほぼ終了しました。LOL
rbaleksandar 2014年

);私は、あなたは、配列の複数のコピー(?関数呼び出しごとであってもよい)より多くのメモリを認識実装を試して働いていたので、あなたは〜16メガバイトのプログラムは、ほとんどあなたのマシンを殺すと思う@rbaleksandar
RSFalcon7

値ではなく参照で渡すため、配列の処理は問題ないと確信しています。同じことがバブルソートでも起こります。地獄、私のクイックソートの実装がバブルソートを吸ったとしても、あなたがおそらく正しく実装できないものです。LOL
rbaleksandar 2014年

LOL基数ソートを試すか、単にstd :: sort :)を使用できます
RSFalcon7 2014年

1
チャンスは無い。これはラボの課題です。:D
rbaleksandar 2014年

3

配列がスタックに割り当てられている場合、この場合、allocを使用して同じサイズの配列を割り当てようとします。


3

配列をスタックに格納するためです。ヒープに保存する必要があります。ヒープとスタックの概念を理解するには、このリンクを参照してください。

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