• ベストアンサー

C++のスタック管理

大きめの配列(2Mくらい)を宣言するとスタックオーバーフローのエラーが発生します。で、配列をstaticにしたらエラーが発生しなくなりました。 なんとなく、初期に大きいバッファを確保するならOKで実行時に確保するとNGなのかな~と思っているのですが、このあたりのことを教えていただけないでしょうか。 なんとなく2Mくらいでオーバーフローが出るのも納得できません。 よろしくお願いします。 環境:VC++ .Net + winXP

質問者が選んだベストアンサー

  • ベストアンサー
  • liar_adan
  • ベストアンサー率48% (730/1515)
回答No.3

2Mの配列を宣言というと void method(){ char a[20000000]; .... } という感じでしょうか。 C/C++言語のメモリは、大きく4つに分かれます。 (1)コード(プログラム)が記録されている領域。 (2)静的記憶領域。(文字列リテラル等が記憶されている領域) (3)スタック領域。 (4)ヒープ領域。 (1)、(2)はこの際関係ないので置いといて、 関数やメソッドの中でふつうに(staticをつけずに) 変数を宣言したとき、そのデータはスタック領域に置かれます。 これに対し、関数の外(グローバル変数)の変数、 static宣言した変数、malloc()で確保したメモリ、 newで作成したオブジェクトは、 ヒープ領域に置かれます。 スタック領域とヒープ領域を比べた場合、 ヒープ領域の方がはるかに大きいのです。 その代わり、スタックの方が効率的にアクセスできるようになっています。 スタック領域は一等地なのです。 山手線沿線に飛行場が作れないのと同じで、 スタックにあまり大きなメモリを確保することは出来ません。 なので、関数・メソッドの中の非static変数(自動変数)に 大きなメモリ領域を確保するのはタブーになっています。

pokapoka1980
質問者

お礼

まさに知りたいことで、勉強になります。 スタックとヒープがごっちゃになっていました。 回答いただいたようなことだったんですね。 ありがとうございました。

その他の回答 (3)

  • neko3839
  • ベストアンサー率37% (100/268)
回答No.4

コンパイラ、リンカのオプションに「スタック サイズの設定」というのがありませんか? VC++.NET は未確認ですが、VC++ 6.0はMSDNのCD-ROMを見るとデフォルト値が1MBとされているようです。 コンパイラ、リンカのオプションでスタックサイズを大きめにするのも1つの対応ではあります。 C++であればnew演算子を使った方が良いと思います。

pokapoka1980
質問者

お礼

オプションがあるんですね。 ちょっと今探しても見つからなかったのですが、 6.0にあるならNETにもあると思いますのであとでもう少し探して見ます。 ありがとうございました。

  • neko3839
  • ベストアンサー率37% (100/268)
回答No.2

スレッド内で使える「スタックサイズ」には制限があります。 上記を自動変数で確保しているなら「スタックオーバフロー」します。 C++の場合は、new で確保してください。

pokapoka1980
質問者

お礼

mallocやnewでの確保も考えたのですが、4次元の配列なので、のちのち(クラスヘッダーを見たときに)分かりやすいと思い、配列での宣言としていました。 newで確保するほうが良いのでしょうか・・。

  • t_nojiri
  • ベストアンサー率28% (595/2071)
回答No.1

UNIXなら、1プロセスが確保できるメモリがカーネルパラメータで定義されてるのですが。 windowsは、うーん、msdn探してみるとかですか。

参考URL:
http://docs.hp.com/ja/B3920-90093/ch08s05.html

関連するQ&A

  • スタック実行の無効化?

    バッファオーバーフロー対策として、スタック実行の無効化という言葉をよく聞きますが、これは具体的にはどういったことなのでしょうか。 スタックやヒープという言葉の意味もよくわかっていないので、教えてください。 よろしくお願いします。

  • .NET(C#)でのスタックオーバーフロー回避方法

    ASP.NET初心者です。初めて質問させていただきます。 ASP.NET(C#)でwin32系の外部ライブラリを使用したいのですが、そのライブラリを読み込むところで スタックオーバーフローが発生してしまいます。 同じライブラリをWindowsアプリケーション(C#)で使用したところ問題なく実行できました。 ASP.NETで同じように使用しようとするとスタックオーバーフローが発生してしまう状態です。 [デバッグ中のエラーメッセージ] xxxx.dllを読み込めません。再帰が深すぎます。スタックがオーバーフローしました。 [イベントログ] 障害が発生しているアプリケーション名: w3wp.exe、バージョン: 7.5.7600.16385、タイム スタンプ: 0x4a5bcd2b 障害が発生しているモジュール名: xxxx.dll_unloaded、バージョン: 0.0.0.0、タイム スタンプ: 0x4d92e27c 例外コード: 0xc0000005 xxxx.dllは、今回使用したいDLLです。 過去の質問等でスタックオーバーフローが発生した場合の解決策を探してはみたのですが、見つけられた方法が以下の2つでどちらもうまくいきませんでした。 (1)スタック領域を大きくする →スレッドでスタック領域を指定して実行してみました。 (2)ライブラリの中身をスタックオーバーフローが起きないように変更する → ライブラリの中身は変更できない状態です。 (1)のほうは、指定方法間違えてるかもしれません。。。 Thread newthread = new Thread(new ThreadStart(init), 0); もしくは、w3wp.exeでエラーが発生しているということはIIS関係で何かおかしなことをしてしまってるのかもとか・・・。 根本から見方が間違ってるかもしれませんが、(1)の方法の正しいやり方、もしくはそれ以外の解決策をご教授頂きますようよろしくお願いいたします。

  • 最大スタックサイズを大きくすることの影響は?

    再帰呼び出しを行うプログラムでスタックオーバーフローが発生するようになりました。 そこで最大スタックサイズを変更しようと考えていますが 最大スタックサイズを大きくすることで何か影響があることはあるのでしょうか? 他アプリ等に影響が出ないかを懸念しています。 ※最大スタックサイズは最大で16Mらしく、現在は1Mです。  特に影響がないのであれば最初から16Mにしておけば良いような気もして疑問に思っています。

  • スタック

    次のプログラムにおいて、fact(5)を実行している時のスタックの様子が分かりません。また、どのような値を持つどのような要素が積まれているかを教えてください。 #include <stdio.h> int fact(int k){ static int l=1; int m = k; if(k==0) return l; else { return fact(m-1)*m; } } main(){ int n=10; int k; k = fact(n); printf("%d\n", k); } よろしくお願いします。

  • VC++ 再帰呼び出しについて

    VC++6.0にてプログラミングを行っているものですが、 関数の再帰呼び出しについて質問です。 再帰呼び出しの際にスタックに積まれる変数というのは、 再帰呼び出しをする関数に渡す引数のことですか? スタックオーバーフローを起こさないために、 staticなポインタにHeap領域上の 変数を割り当てるとよい。 と分かったのですが、 この意味は、例えば static int *a = new int; ということなのですか?

  • Visual C++ 2008 オーバーフローのエラーについて

    プログラムのデータ量を増やしたところ、エラーが起こりました。 減らせば流れることから、スタックオーバーフローと呼ばれるものかと思います。 スタックサイズが初期値のままなのが原因だと予想したのですが、この変更方法がどうにもわかりません。 もしよろしければ、スタックサイズの変更方法を教えてください。 また、別の対処方法をご存知でしたらそちらでも結構です。 ソフトはMicrosoft Visual stdio 2008のC++です。 どうぞよろしくお願いします。

  • スタック領域エラーについて

    いつも参考にさせていただいています。 ご回答よろしくおねがいします。 新規のフォームにチェックボックスを3つ追加しました。 コントロール配列です。 ------frame1-------- | checkbox1(0) | | checkbox1(1) | | checkbox1(2) | -------------------- このときに、 --------------------------------------------- Private Sub Check1_Click(Index As Integer) Check1(0).Value = 0 Check1(1).Value = 0 Check1(2).Value = 0 Check1(Index).Value = 1 End Sub --------------------------------------------- 以上のようにプログラムを実行すると、 スタック領域エラー(エラー28)が出力されます。 この回避方法について、また原因がおわかりいただけたら ご教授おねがいできないでしょうか? OS:WinXP SP3 環境:VB6

  • スタックオーバーフロー

    はじめまして!SH-4を使用して操作パネル処理の学習をしている新米です(^^;) 現在、操作パネルのSW操作から、押されたSWによりパネル上に設けた液晶画面に文字を表示するというプログラムをコーディングしています。 そこで以下をコーディングしたところ、「これではSTACKオーバーフローが発生してしまう可能性がある」との指摘を受けました。。。そこで回避(対策?)する方法を考えているのですが、なかなか思い浮かびません。。。なにか良い方法はあるでしょうか? コーディングは以下の感じです。 *****バッファの宣言***** void (*pAutoFunc)(); // Auto SW が押された場合に実行する関数へのポインタ void (*pManuFunc)(); // MANU SW が押された場合に実行する関数へのポインタ void (*pStartFunc)(); // START SW が押された場合に実行する関数へのポインタ void (*pDecFunc)(); // DEC SW が押された場合に実行する関数へのポインタ void (*pIncFunc)(); // INC SW が押された場合に実行する関数へのポインタ void (*pF2Func)(); // F2 SW が押された場合に実行する関数へのポインタ void (*pF1Func)(); // F1 SW が押された場合に実行する関数へのポインタ *****メインループ***** void MenuMng(void) { while(1) { // スイッチリード、etc... Convert(); if(SwData & STOP_SW_ON) { Stop(); } else if(SwData == MANU_SW_ON) { (*pManuFunc)(); } else if(SwData == AUTO_SW_ON) { (*pAutoFunc)(); } else if(SwData == START_SW_ON) { (*pStartFunc)(); } else if(SwData == INC_SW_ON) { (*pIncFunc)(); } else if(SwData == DEC_SW_ON) { (*pDecFunc)(); } else if(SwData == F1_SW_ON) { (*pF1Func)(); } else if(SwData == F2_SW_ON) { (*pF2Func)(); } } } 処理としては以下を考えています。 1.初期化として、一度Stopを実行する。Stopは初期画面の表示と、初期画面でそれぞれSWが押された時にとぶ関数の先頭アドレスを  確保する。 2.Convert関数内で、SWの状態を読み出し、SwDataに確保する。 3.押されたSWを判定して、確保してある関数の先頭にとんで処理を行う。 4.とんだ関数内pManuFunc、pAutoFunc等に、次にそれぞれSWが押された場合にとぶ関数の先頭アドレスを確保する。 5.処理終了で戻ってきて、2.から5.を繰り返す。 STOP SW が押された場合は、どんな画面になっていても初期画面に戻ります。1度処理を実行したら、SWが全て放されるまで SWが押されていても処理は実行しません。 以上です。宜しくお願い致します(@_@;;)

  • C言語の基礎的な質問---文字配列の初期化

    C言語の配列の初期化に関する質問です。 もし規格によって回答が異なる場合は、ANSIのCということにしてください。 関数の中に、 char str[ ]="ABC"; (イ) という宣言があるとします。(staticは付きません。) これは、 char str[ ]={'A', 'B', 'C', '\0'}; (ロ) と全く同じ意味でしょうか。  似て非なるものに char *str="ABC"; (ハ) というものがあります。この場合は、 strとは違うところに"ABC"('C'の次には'\0'があります。)という領域が確保されていて、 その先頭アドレスでstrが初期化されるのですよね。 (イ)(ロ)(ハ)のいずれの場合も関数の中に書かれているとすれば、 いずれもstrは自動変数で、関数実行時にstrの領域が確保されますよね。 (イ)は配列strの領域が確保されるときに、 配列strとは別のところにある"ABC"という領域の内容を、コピーして設定する、 ということでしょうか。 (ロ)は、配列の領域確保時にstr[0]を'A'で、str[1]を'B'で、str[2]を'C'で、str[3]を'\0'で、初期化する、 ということで、 配列とは別のところには"ABC"という領域はない、 という考えでよろしいでしょうか。 もしそうだとしたら、配列とは別のところに"ABC"という領域があるかどうかという点で(イ)と(ロ)は異なることになりますが、そう考えてよろしいのでしょうか。 それとも、そういうことは処理系に依存することなんでしょうか。

  • 変数の宣言方法の相違によるエラー

    ある変数を関数内で宣言した場合、デバック時に「ハンドルされていない例外が発生しました」っていうエラーになります。でも、グローバル変数として宣言した場合には、同じエラーが発生しませんでした。今回の場合、配列を10,000,000ぐらい確保しようとしたので、メモリ領域を確保できなかったが、エラーの発生原因だと思います。メンバ変数とグローバル変数で確保できるメモリ領域にどれくらいの違いがありますか。その他にクラスのメンバ変数として宣言した場合とグローバル変数として宣言した場合でどのような違いがありますか。 わかる方が教えてください。よろしくお願いします。

専門家に質問してみよう