1. 序章

このチュートリアルでは、コールスタックメカニズムについて説明します。 それがどのように機能するか、それが何をすることを意図しているか、そして有名なスタックオーバーフローエラーがどのように発生するかを調べます。

今日、最も人気のあるプログラミング言語は高レベルであり、それらを使用するプログラマーから多くのものが離れています。 ただし、ほとんどの場合、内部で多くの作業が行われています。 プログラム実行の実際の詳細は、コンパイラ、オペレーティングシステム、および命令自体のタイプによって異なる場合がありますが、それらのほとんどは、一般に「コールスタック」または単に「スタック」と呼ばれるメモリのチャンクを操作します。 これが、少なくともそれを高レベルで見る価値がある理由です。

2. スタック定義

スタックは、一般に、後入れ先出し(LIFO)方式でデータ要素を追加および削除するために使用される抽象データ型です。 例えとして、スタックは、上から本を取り出したり追加したりすることしかできない本のスタックと考えることができます。 コンピュータサイエンスの用語では、スタックに要素を追加することは「プッシュ」とも呼ばれ、最上位から要素を削除することは「ポップ」と呼ばれます。

最初は、要素の追加と削除に制限があることはハンディキャップのように聞こえるかもしれませんが、ネストされたシーケンスを処理する場合、一般的にプログラミングやコンピューターサイエンスでスタックを非常に便利なツールにします。

3. コールスタック

特に、呼び出しスタックは、主に複数の命令を編成し、それらの実行を追跡することを目的としたスタックです。 プログラムが関数を呼び出すとき、プログラムコンパイラ、インタプリタ、またはシステム(実装に応じて)が最初に行うことは、関数が必要な作業を行うためのメモリのチャンクを確保することです。 チャンクはスタックフレームとも呼ばれ、通常、少なくともいくつかのものが含まれています。

  • 渡された呼び出された関数の引数
  • ローカル変数用に予約されたスペース
  • 関数の結果を返す場所を指すために使用されるリターンメモリアドレス

関数が機能するにはスタックフレームが必要なので、実行中に必要なアクセスを提供する方法を考えることが重要です。 しかし、シーケンシャル命令を処理できるようにメモリを整理するにはどうすればよいでしょうか。

4. 簡単な例

たとえば、関数 main を備えた単純なプログラムを考えてみましょう。この関数には、 greet という別の関数が含まれており、それ自体が print( “Hello”)関数を呼び出します。

システムはそれぞれのメモリを予約しますが、一度に実行できるのは1つだけなので、コンピュータはどの機能を最初に実行するかをどのように知るのでしょうか。

greetを実行せずにmainを実行することはできません。また、printを実行しないとgreetを実行できません。 そして、これはスタックデータ構造が役立つところです。 関数が呼び出されると、その関数がアクティブになり、そのフレームを呼び出しスタックの一番上にプッシュします。 さらに、新しい関数が呼び出されるたびに、そのフレームがスタックの一番上にすぐにプッシュされ、アクティブなフレームになります。 このようにして、一度に1つのアクティブな関数のみが存在するようにし、プロセスをスレッドセーフにします。

関数の実行が終了すると、フレームをポップします。 また、スタックフレームには、それと呼ばれる関数のスタックフレームを指すリターンアドレスが含まれているため、親関数に簡単に戻すことができます。 それがスタック構造の美しさです。

これは、私たちを迎える単純なプログラム中のメモリ内のコールスタックフレームを表示する図です。

5. スタックオーバーフロー

単純な例では、3つの関数が相互にネストされていますが、プログラミングでは、プログラムのロジックを抽象化する傾向があるため、ネストがさらに多くなることがあります。 また、実行時に、呼び出しスタックが予約済みメモリを使い果たしてエラーが発生することがあります。 このタイプのエラーをスタックオーバーフローと呼びます。 一般に、このタイプのエラーはリソースプロビジョニングの問題を示しており、実行時に発生するため、理解するのが難しい場合があります。

スタックオーバーフローの最も一般的な原因は、過度に深い再帰または無限の再帰です。 このような場合、関数はそれ自体を何度も呼び出すため、個々のスタックフレームを格納するために必要なスペースはスタックのサイズよりも大きくなります。

実行が確実に失敗する場合の無限再帰の例を次に示します。

6. 結論

この記事では、コールスタックを調べ、それに関連する用語を説明し、プログラミング全般でどのように使用されるかを探りました。