PowerPoint プレゼンテーション

オブジェクト指向プログラミング
と開発環境
2007.11.15
2007年度担当 中井央
プログラム実行の様子
• ユーザがプログラムを起動
• OSはそれを受け取り、プログラムの実行
を開始
• プログラムが再帰関数呼び出しに対応
するにはその仕組みが必要
• 実行時記憶
– プログラムに割り当てられたメモリの使い方
実行時記憶
• 一般的な実行時記憶の割り当て方
– 目的コード、静的データ、実行時
目的コード
スタック、ヒープからなる
– 実行時スタック、ヒープは実行の様子 静的データ
によって動的に使用領域が変化する 実行時スタック
– 実行時スタックは関数呼び出しに
対応する
– ヒープは動的なメモリ割当に使われる
ヒープ
再帰呼び出しの仕組み
f(5)
int fact(int n){
if (n < 2) return 1;
else return n * fact(n - 1);
}
n*24 = 120
n=5
n * f(4)
n*6 = 24
それぞれの呼び出し
における n を
覚えているから
計算ができる
n=4
n * f(3)
n*2 = 6
n=3
n * f(2)
n*1 = 2
n=2
n * f(1)
1
n=1
1
再帰呼び出しの仕組み(2)
f(5)
戻ってくるときには
n は 1 になっている
ので正しい答えが
得られない!!
n*1 = 1
n=5
n * f(4)
n*1 = 1
n=4
n * f(3)
n*1 = 1
n=3
n * f(2)
呼び出しごとに
n が上書き
されたら
n*1 = 1
n=2
n * f(1)
1
n=1
1
再帰呼び出しの仕組み(3)
• スタックを使う
main
f(5), n = 5
f(4), n = 4
f(3), n = 3
f(2), n = 2
それぞれの呼び出しの
n を別々に保存
呼び出しが終われば
割り当てられた領域は
スタックから下ろされていく
f(1), n = 1
図ではスタックは
下に伸びていく
プログラムを間違えて
無限再帰を起こすと
使用できるメモリを
食いつぶしてしまう
入れ子型の関数宣言
• C言語では許していない
• Pascal 他の言語ではサポート
– サンプルプログラムは次のスライド参照
– 1つ内側の関数は参照可能
⇒それより内側は見えない
⇒内側からはそれを囲む名前は参照可能
Pascal プログラムの例
sample.p
1 program test(input, output);
2
3 procedure a;
4
5 var x : integer;
6
7 procedure c;
8
begin
9
writeln('c');
10
if 0 < x then
11
begin
12
x := x - 1;
13
c();
14
end;
15
end;
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
begin
x := 3;
c();
writeln('a');
end;
procedure b;
Begin
writeln('b');
end;
begin
a();
b();
end.
活性レコード
• 関数呼び出し実現のためのスタックの要素
• 各関数呼び出しごとに割り当てられる
– 活性レコードの割当=活性化
– 実行時のスタックの変化の例は次のスライド
• 主な要素
•返り値
•引数
•動的リンク
•静的リンク
•戻り番地
•レジスタの退避場所
•局所データ
•一時的な値
実行時のスタックの変化の様子
(sample.p)
QuickTimeý Dz
TIFF (LZW) êLí£ÉvÉçÉOÉâÉÄ
ÅB
ǙDZÇÃÉsÉNÉ`ÉÉǾ å©ÇÈÇ žÇ½Ç …ÇÕïKóvÇ­Ç•
局所変数の値の参照
• 活性レコードは1つの関数呼び出しに対応
• 関数内で宣言された変数はそれに対応
する活性レコードに割り当てられる
• 参照は活性レコード内の基準位置からの
オフセットによる 関数aの固定領域の始まり
y の位置は基準から
z の分+yの分
戻った位置
変数 x の領域
変数 y の領域
変数 z の領域
動的に変化する領域の始まり
:
スタックトップ
基準
非局所変数の参照
• 入れ子の深さ (レベル)
– メインを0, 入れ子が深くなるに
つれ、1つずつ大きくなる
• 内側からはその外側の名前
は参照可能
• どの活性レコードの値を参照
すればよいか??
0
1
2
3
4
1
5
6
2
7
3 8
9
10
11
12
13
14
15
16
たとえば c の中から
a で宣言された変数を参照したい
program test;
var x:integer;
procedure a;
procedure b;
procedure c;
begin
end; { c }
begin
end; { b }
begin
end; { a }
begin
end.
入れ子型静的スコープでの関数
1 program test;
呼び出しの性質
2
• 最初はメイン (レベル0)
• 呼び出しパターンは2通り
– 呼び出し側 p, 呼ばれた側 q と
する
– p < q なら q = p + 1
– p≧q
内から外は
どのレベルでも
OK
外から内は
1つ内側だけ
3
4
5
6
7
8
9
10
11
12
13
14
15
16
var x:integer;
procedure a;
procedure b;
procedure c;
begin
end; { c }
begin
end; { b }
begin
end; { a }
begin
end.
静的リンクの設定
• a→b→c→b→c と読んだ時の実行時スタッ
ク
• p<q の場合
a
b
c
b
c
– a→b や b→c の場合:
呼び出しもとを指せばよい
• p≧q の場合
– c→b や c→a などの場合:
呼び出しもと(直前の活性
レコード) から p-q+1 回静的
リンクを辿ったところ
関数の引数の渡し方
• 値渡し
– C 言語で関数を呼ぶ場合: f(n) を f(a)で
呼んでも、fの本体で n の値をいくら変えても
呼び出しもとの a の値は変わらない
• 参照渡し
– 渡すのは、変数のアドレス
– C だと f(&a) のような形
– 変数の在処を渡すので、関数本体でその中身
を書き換えれば、呼び出し元の変数の中身が
書き変わる
関数呼び出し時の処理
•
呼び出す側の処理
1. 引数の評価、その値を適切な位置へ格納
2. 呼び出される側の活性レコードに現在の
活性レコードへのポインタを格納
3. 静的リンクの設定
4. 戻り番地の設定
5. 呼び出される側へ制御を移す
•
呼ばれた側の処理
1. レジスタの退避
2. 局所データの初期化
3. スタックトップの設定
関数呼び出しからの戻り時の処理
•
呼ばれた側
1. 返り値を所定の場所へ格納する
2. 退避したレジスタの復元
3. スタックトップを呼び出しもとのトップへ戻す
(保存しておいた活性レコードの復元)
•
呼び出し側
•
返り値を取り出す
他のフェーズとの関連
• 実行時環境に合うよう、意味解析等で情報
を作っておく必要がある
– 変数の領域を計算するには型とそれに必要な
サイズを計算しておく
– オフセットを計算する
Java についていくつか
• static がついた変数はプログラム実行中に
は一カ所だけ値の保存場所が用意される
• メソッド内のローカル変数はメソッド呼出し
ごとにスタックフレームに確保される
• オブジェクトは new の実行によりヒープに
確保される
• 例外処理:例外発生時、catch できるところ
までスタックをさかのぼる
その他
• 無限再帰をしたらどうなるか
class Test2 {
static void r(){
r();
}
public static void main
(String args[]){
r();
}
}
% java Test2
Exception in thread "main"
java.lang.StackOverflowError
at Test2.r(Test2.java:3)
at Test2.r(Test2.java:3)
at Test2.r(Test2.java:3)
at Test2.r(Test2.java:3)
...
1024回の
呼出し後、停止
さらなる学習について
• 実行時環境の理解
• プログラムの動作の理解
• よりよいプログラム記述が可能
• さまざまなプログラミング言語 and/or その
コンパイラの仕組みも知ろう!