関数とスタックの関係をRustで整理する
前書き
Rustでは所有権という概念があり、所有権を理解するためにはスタックとヒープを理解していなければならない。
まずはスタックについての理解を深めていきたいと思う。
今回の記事を執筆する上では
以下の記事が関数とスタックの関係を理解する上で非常に有益でした。
https://www.uquest.co.jp/embedded/learning/lecture07-1.html
自分の理解のため書き直したりしているところ以外は上記の記事内容と大差ありません。(言語はRustに置き換えています)
スタックとは
スタックとは、データを保持するための構造の1つ。
スタックに要素を追加することを「Push」、
スタックから要素を取り除くことを「Pop」という。
最後に入った要素が最初に取り出される。(以下参照)
空のスタック [
Aをスタックに追加 [A
Bをスタックに追加 [A B
Cをスタックに追加 [A B C
Cをスタックから取り出し [A B
Dをスタックに追加 [A B D
スタックから2回取り出し [A
スタックメモリ
プゴグラムの実行単位(タスク・スレッド)ごとにスタックメモリという特別なメモリ領域が割り当てられ、そのプログラム内だけで使われる変数やアドレス情報を置いておくためのメモリ領域のこと。
タスクが終了、または関数から抜けるとスタックが破棄される。
グローバル変数などは他の関数などからも参照できるように別の領域に置かれている。
関数とスタック
以下のプログラムを例に関数とスタックの関係を勉強する。
fn functionB(){
let b1 = 200;
let b2 = 300;
println!("functionB {}, {}", b1,b2);
}
fn functionA(){
let a1 = 100;
functionB();
println!("functionB {}", a1);
}
fn main(){
functionA();
}
---
Finished dev [unoptimized + debuginfo] target(s) in 0.48s
Running `target/debug/guessing_game`
functionB 200, 300
functionB 100
【関数呼び出しシーケンス】
main()
┗ functionA()
┗ functionB()
①CPUはfunctionAにある変数「a1」をSP(スタックポイントレジスタ)にPushする
┗ SP = 1 FP = 0 [a1
②functionBを呼び出し、functionBから帰ってきたアドレス(=リターンアドレス)
をスタックにPushする
┗ SP = 2 FP = 0 [a1 retB
③FPレジスタの値をスタックにPushして、現在のSPレジスタの値をFPレジスタ
にコピーする。これでfunctionBを呼び出す準備が完了しfunctionBのアドレスにジャンプする。
┗ SP = 3 FP = 3 [a1 retB 0
④functionBの変数「b1,b2」をスタックにPushして呼び出し元のfunctionAに戻る。
┗ SP = 5 FP = 3 [a1 retB 0 b1 b2
④-1 戻るためにはスタックにPushしておいたretBを取り出してジャンプする。
ただし、functionBで宣言する変数が毎回2とは限らないため、funtionBにジャンプ
する前にFPレジスタに保存しておいた値をSPレジスタに設定する。
b1,b2はスタックの管理外となる。
┗ SP = 3 FP = 3 [a1 retB 0
④-2 現時点でPopするとfunctionAにおけるFPレジスタの値だった「0」
を取り出すことができる。
┗SP=2 FP=0 [a1 retB
もう一度PopするとretBを取り出せる。
┗SP=1 FP=0 [a1
これでfunctionAを処理していた時の状態に戻り、retAにジャンプすれば処理が完了する。
この記事へのコメントはありません。