スタックオーバーフローって、そもそもナニ?

はじめに

プログラミングをしていると、「スタックオーバーフロー」という言葉を耳にすることがあります。
「なんだか怖そう……」「ウイルスみたいなもの?」と思うかもしれませんが、実はこれ、プログラムの仕組みの中で起こる単純な“メモリの問題” なんです。

今回は、難しい専門用語をなるべく使わずに、「スタックオーバーフローとは何か」をわかりやすく見ていきましょう。

最初に「スタック」とは?

“スタック(stack)”とは、英語で「積み重ねる」という意味。
プログラムの世界でも「積み重ねるようにデータを管理する仕組み」のことを指します。

たとえば、

コンピュータが関数(処理のかたまり)を呼び出すたびに、「次にどこへ戻るか」や「使っている変数の情報」などを、スタックという特別な領域に順番に積み重ねていきます。

これはまるで、「お皿を上にどんどん積んでいく」ようなもの。最後に積んだお皿から順に片付けていく──これが“スタック”の基本ルールです。
(この動きを専門用語で「LIFO:Last In, First Out」と言います)

オーバーフローとは?

“オーバーフロー(overflow)”は、「あふれる」という意味です。
つまり、「スタックオーバーフロー」は、スタックにデータを積みすぎて、入りきらなくなり、あふれ出した状態

を指します。たとえば、 お皿をどんどん積みすぎて、タワーが崩れてしまうようなものです。
プログラムにとっては非常に危険な状態で、多くの場合、実行中にエラーを出して強制終了します。

どうしてそんなことが起きるの?

一番よくある原因は、再帰の使いすぎです。

再帰とは?

関数の中で「自分自身を呼び出す」ことを言います。

少し便利なテクニックなのですが、
もし「終わりの条件」を正しく書かないと、永遠に自分を呼び続けてしまうことがあります。

def hello():
print("Hello!")
hello()  # ← 終わりがない!

このプログラムを実行すると、hello() が自分を呼び出し続けて、ずーっと止まりません。
関数を呼び出すごとにスタックにデータが追加されていくため、やがてスタックがいっぱいになり、
「スタックオーバーフロー」というエラーが発生します。

実際のプログラムでは?

多くの言語では、スタックの大きさに上限があります。
「1回呼び出すごとに少しずつメモリを使う」ため、それを超えるとシステムが

「もう無理!これ以上は積めません!」と叫ぶ――

それがスタックオーバーフローです。

Pythonでは「RecursionError」というメッセージが出たり、C言語やC++では「segmentation fault(セグメンテーションフォルト)」と表示されたりします

どうしたら防げる?

スタックオーバーフローは、ちょっとした工夫で防げます。

  • 再帰を使うときは、必ず終了条件を書くこと!
  • 深すぎる関数のネスト(入れ子)を避けること!
  • 必要なら、ループ(forやwhile)で書き直すこと!

プログラムを整理して、
「ちゃんと終わる処理」になっていれば、オーバーフローを起こすことはありません。

まとめ

用語意味
スタック関数などを「積む」ための領域
オーバーフローあふれること
スタックオーバーフロースタックにデータを積みすぎてあふれた状態

「スタックオーバーフロー」は難しいエラー名に見えますが、本質は「積みすぎてあふれた」というシンプルな現象です。 慣れてくると、「あ、再帰が止まってないんだな」などとすぐピンと来るようになります。
エラーに出会ったときは、慌てずに「どこで積みすぎているのか?」を確認してみましょう。

余談:名前が同じあのサイトは?

実は、有名なQ&Aサイト 「Stack Overflow」 は、まさにこのエラー名から命名されています。
「みんなの質問や知識があふれ出す場所」──という意味が込められているようです。