😽

関数の呼び出しと戻りの流れについてのメモ

に公開

はじめに

再帰処理を実装している中で、途中スタックオーバーフローが起こりました。
スタックオーバーフローがどういうものかについては概念的に知っていましたが、
関数を複数回呼び出す際、データがどのようにやり取りされているのか、その点が不明でした。
その仕組みについて調べてわかったことを簡単にまとめました。

関数内で別の関数を呼び出すケース

1. 関数呼び出し

呼び出し元の関数から、別の関数(呼び出し先)が呼び出されます。
このとき、呼び出し元に戻るべきアドレスや引数を格納したスタックフレームがスタックにプッシュされます。

2. 実行

CPUは、呼び出し先の関数を実行します。
このとき、呼び出し先の関数にポインタがあります。

3. 戻り値

呼び出し先の関数が処理を終えると、その結果(戻り値)をCPUレジスタに格納するか、あるいはスタックを通じて渡します。

※関数の処理結果である戻り値の渡し方は2種類
① 戻り値が小さい場合: CPU内のレジスタに一時データとして格納する
② 戻り値が大きい(大きな構造体など)場合: スタックを通じて呼び出し元のスタックフレームにコピーされる

4. スタックフレームの解放

呼び出し先の関数は、スタック上の自身のスタックフレーム(内部にあるローカル変数など)を解放します。

5. 実行の再開

CPUは、スタックに保存されていた戻り先アドレスを読み取り、呼び出し元の関数の実行を再開します。
呼び出し元の関数は、レジスタやスタックを通じて受け取った戻り値を次の処理で利用します。

わかったことまとめ

  • ある関数から別の関数への処理結果(戻り値)の渡し方には2種類あること
  • CPUレジスタ内に保存する場合は、メモリへのアクセスがない分高速に処理できること
  • スタックフレームに渡す場合は、CPUがスタック領域にアクセスしてデータを取り出して処理する分、レジスタに保存したものを使用する場合に比べると低速処理であること

最後までお読みいただき、ありがとうございました。

Discussion