C++では、スタックオーバーフローは通常、プログラムの回復不能なクラッシュを引き起こします。本当に堅牢である必要があるプログラムの場合、これは特にスタックサイズが制限されているため、許容できない動作です。問題の処理方法に関するいくつかの質問。
一般的な手法でスタックオーバーフローを防ぐ方法はありますか? (スケーラブルで堅牢なソリューション。これには、大量のスタックを消費する外部ライブラリーの処理などが含まれます。)
スタックオーバーフローが発生した場合に対処する方法はありますか?スタックは、そのような問題に対処するハンドラーができるまで巻き戻されます。
そこには、拡張可能なスタックを備えたスレッドを持つ言語があります。 C++ではそのようなことは可能ですか?
C++の動作の解決に関するその他の役立つコメントをいただければ幸いです。
スタックオーバーフローの処理は適切な解決策ではなく、プログラムがスタックをオーバーフローしないようにする必要があります。
スタックに大きな変数を割り当てないでください(「大きな」とはプログラムによって異なります)。再帰アルゴリズムが既知の最大深度の後に終了することを確認してください。再帰的アルゴリズムが未知の回数または多数回再帰する可能性がある場合は、(独自に動的に割り当てられたスタックを維持することによって)再帰を自分で管理するか、再帰的アルゴリズムを同等の反復アルゴリズムに変換します。
「本当に堅牢」でなければならないプログラムは、「スタックを大量に消費する」サードパーティまたは外部のライブラリを使用しません。
一部のプラットフォームは、スタックオーバーフローが発生したときにプログラムに通知し、プログラムがエラーを処理できるようにすることに注意してください。たとえばWindowsでは、例外がスローされます。この例外はC++例外ではありませんが、非同期例外です。 C++例外はthrow
ステートメントによってのみスローされますが、非同期例外はプログラムの実行中にいつでもスローされます。ただし、スタックオーバーフローはいつでも発生する可能性があるため、これは予想されます。関数呼び出しまたはスタック割り当てによってスタックがオーバーフローする可能性があります。
問題は、スタックオーバーフローが原因で、例外のスローが予期されていないコード(たとえば、C++でnoexcept
またはthrow()
とマークされている関数)からでも非同期例外がスローされる可能性があることです。したがって、この例外を何らかの方法で処理しても、プログラムが安全な状態であることを知る方法はありません。したがって、非同期例外を処理する最善の方法は、それをまったく処理しないことです。(*)。スローされた場合は、プログラムにバグがあることを意味します。
他のプラットフォームでも、スタックオーバーフローエラーを「処理」するための同様の方法がある場合がありますが、そのような方法では同じ問題が発生する可能性が高くなります。
(*)非常にまれな例外がいくつかあります。
次のような適切なプログラミング方法を使用して、スタックオーバーフローから保護できます。
それらは私が過去数年間見た中で最もSO原因です。
自動SOを見つけるには、いくつかの静的コード分析ツールを見つけることができるはずです。
C++は強力な言語であり、その力を利用して自分の足を撃つことができます。スタックオーバーフローが発生したときに検出および修正/中止するための移植可能なメカニズムについては知りません。確かに、このような検出は実装固有のものです。たとえば、g ++は-fstack-protector
スタックの使用状況の監視に役立ちます。
一般に、最善の策は、大きなスタックベースの変数を避け、再帰的な呼び出しに注意することです。
Re:拡張可能なスタック。次のようなコードを使用して、スタックスペースを増やすことができます。
#include <iostream>
int main()
{
int sp=0;
// you probably want this a lot larger
int *mystack = new int[64*1024];
int *top = (mystack + 64*1024);
// Save SP and set SP to our newly created
// stack frame
__asm__ (
"mov %%esp,%%eax; mov %%ebx,%%esp":
"=a"(sp)
:"b"(top)
:
);
std::cout << "sp=" << sp << std::endl;
// call bad code here
// restore old SP so we can return to OS
__asm__(
"mov %%eax,%%esp":
:
"a"(sp)
:);
std::cout << "Done." << std::endl;
delete [] mystack;
return 0;
}
これはgccのアセンブラー構文です。