種類: プログラム実行時のエラー。
原因: ここで紹介する事例の場合、巨大な配列を自動変数(ごく普通の変数)として宣言したのがよくなかったようです。
対策: 大きな配列が必要なときは、ポインタ変数に十分なメモリを割り当てて(malloc関数)、配列の代用品とするようにしました。
自動変数として宣言された変数のデータは、メモリ上のスタックと呼ばれる場所に保管されます。
スタックの容量は、デフォルトでは2500バイトと決められています(この辺はあくまでもLSI-Cの話)。
データの量が多すぎてスタックに入りきらなくなってしまった状態のことを、スタックオーバーフロー(stack overflow)といいます。
このとき、往々にして、スタックからはみ出した分のデータが周辺のメモリの内容を破壊してしまうため、プログラムは予期せぬ動作を行なうようになります。
あり得ない結果が表示される場合や、OSごと固まってしまう場合、まったく無関係なエラーメッセージが表示される場合もあれば、たまたま正常な処理が行なわれる場合もあります。
コンパイラの設定次第では、スタックがあふれた時点でプログラムを停止させることも可能ですが(lcc.exeの「-h」オプション)、それでも暴走するときは暴走するので、あまり当てにはなりません。
このエラーを解消する方法は2通りあって、その1つはスタック自体の容量を増やすこと(マニュアルの3章9節など)、もう1つはスタックに入れるデータのほうを減らすことです。
このうち後者は、配列のサイズが大きすぎるときなどに有効な方法で、具体的には、自動変数のかわりにmalloc関数で獲得したメモリや、静的(static)変数を使用します。
自動変数以外では、関数の引数や戻り値のデータもスタックに保管されることになっています。
関数の再帰呼び出しの入れ子が重なりすぎると、たとえそのなかで自動変数を使っていなくてもスタックがあふれてしまう場合があるので、注意が必要です。
「NULL pointer assignment」というメッセージにだまされて、苦労しました。 とかそういう話。
スタックオーバーフローの予防策を探る実験、3つ。
関数の呼び出しとスタック消費の関係を調べる実験。
関数の再帰呼び出しによってスタックオーバーフローを起こす実験。
さまざまな条件下におけるメモリの使われ方を観察する実験。