プログラミング言語論 A
(Concepts on Programming Languages)
趙 建軍
(Jianjun Zhao)
1
命令型言語 (4)
(Imperative Programming Languages)
手続き(関数)の呼び出し
2019.05.30
第7回
3
今日の講義
手続きとは
手続きの定義
引数渡し
スコープ規則
今日の講義
手続きとは
手続きの定義
引数渡し
スコープ規則
手続きとは
プログラムの一部に名前を付けるものである
その部分のことを手続きの本体という
手続きが呼ばれると、手続きの本体が実行さ
れる
この手続きの本体の実行のことを手続きのactivation
という
関数 (function)
は、値を返す手続きのことであ
る
関数、手続きという用語を区別をしないで用いる
場合もある
5手続きとは
手続き: 一連の処理を行う機能単位
手続き(関数)が名前をつけることができ,
その名前で何度でも
呼び出す
ことができる
名称
言語
サブルーチン(subroutine)
FORTRAN, Basic
手続き(procedure)
Pascal
関数 (function)
C, C++, Go
手続きとは
手続きの必要性
システムの大規模化、複雑化
要求機能を、より簡素化された複数の機
能に分解し、各々を手続きとして実装す
る。
これらを組み合わせて全体を実現
=
モジュール化
7手続きの構文
手続きの定義
手続きにおける引数は
仮引数 (formal parameter)
と呼ばれる
手続き呼び出しにおいて、引数が0個でも括
弧が必須の場合が多い
C, Modula-2など
Pascalでは引数がない場合は括弧を書かない
<
手続き名
>
(
<
仮引数列
>
)
手続きの構文
手続き呼び出し文の構文
手続き呼び出し文における引数は実引数
(actual parameter)と呼ばれる
<返り値の型名>
<手続き名>
(
<実引数列> )
9手続きの呼び出し
関数の戻り値
11
手続き呼び出しの例
int plus(int z, int y) { int d = 0; while (d < y) { z++; d++; } return z; } main() { int z = 0; int c = 0; while (c < y) { z = plus(z, y); c++; } 関数plusの定義(関数宣言) 関数plusの呼出し
手続きの型
int plus(int z, int y) {
int d = 0; while (d < y) { z++; d++; } return z; } main() { int z = 0; int c = 0; while (c < y) { z = plus(z, y); c++; } }
すべての関数は型を持つ。
関数定義と呼出し側で型が
一致してなければならない
13値を返さない手続き
値を返さない手続きは以下のように定義する
Java, C, C++: 戻り値を
void
に
VB:
サブルーチンプロシージャとして定義
例
void
sample(int x) {
x:= x+1:
printf(“%d¥n”, x);
}
main() {
sample(9);
式として扱われない
(値を評価できない)
再帰呼び出し(1)
関数の中で関数を呼出すことが可能
main() {
printf(“%d¥n”, max3(12, 15, 11)); }
int max3(int x, int y, int z) { return max2(max2(x, y), z); }
int max2(int x, int y) { if (x >= y) return x; else return y; } 15
再帰呼び出し(2)
関数の中で自分自身を呼び出す
int
factorial(int x) {
if
(x == 1) {
return
1;
} else
{
return
x * factorial(x – 1);
}
}
17
今日の講義
手続きとは
手続きの定義
引数渡し
スコープ規則
引数 (parameters)
仮引数
(formal parameters)と
実引数
(actual parameters)
仮引数と実引数の対応付け
その位置によって対応付ける: positional parameters
仮引数の名称を指定: keyword parameters
引数に関するその他のしくみ
デフォルト値を持つ引数
仮引数と実引数の個数が異なる
実引数が足らない → 未定義を許す
実引数があまる
→ 複数の実引数をリストにしてまと
引数結合方法
C 言語での引数結合の例
宣言
呼び出し
19
int func(int x, double y) {・・・}
func( n, x + 3.5 );
引数渡しの原理
実引数を仮引数に渡す方式には複数ある
引数の渡し方式
Call-by-value (値呼び、値渡し)
Call-by-reference (参照呼び、参照渡し)
Call-by-name (名前呼び、名前渡し)
どのように引数を渡すのですか?
via. the
stack
via. the
registers
21
値呼出し(call-by-value)
仮引数には実引数の評価結果の値が渡される
1.
実引数の値を求める
2.
仮引数は、手続き内の局所変数として生成する
3.
実引数の値をその変数の初期値とする
4.
手続きの本体を実行する
手続き内で局所変数の内容が変化しても、呼出し
側に影響しない
局所変数の生成と値のコピーの必要がある
値呼出し(call-by-value)
C 言語は、値呼出しの機能のみである
例
宣言
呼出し
int func(int x, double y) {・・・}
23
参照呼出し(call-by-reference)
仮引数には実引数のアドレスが渡される (渡された
アドレスが仮引数のアドレスとなる)
仮引数への変更は、実引数に反映するので、結果
を返すためとして使える
局所変数の生成、コピーの必要がない
実引数は変数等であり、式は許されない
変数の衝突に注意が必要
変数呼出し
(call by variable) ともいう
名前呼出し(call-by-name)
手続きの本体の中の仮引数を実引数の式で置き換えて
実行する
この置き換えで名前の衝突が起こる場合は、名前の付
け替えを行う
1. 手続きの本体にある仮引数を、対応する実引数の式で置換す る 2. ただし、これによって手続き内の局所変数と衝突する場合は、 局所変数の名前の付け替えをする 3. できあがった手続き本体を、手続き呼出しのところに挿入す る 4. ただし、この挿入によって手続き内の大域変数が呼出し側の 局所変数と衝突する場合は呼出し側の局所変数を付け替える 5. そのまま実行する
名前の衝突
実引数と手続き内の局所変数との衝突 手続き内の大域変数と呼出し側の局所変数の衝突25
名前呼出しの例
呼出しプログラム begin integer i, n; n:=n+1; p(A[i]) … end; proc p(x) begin integer i; x:=x+i+n end; begin integer i; A[i]:=A[i]+i+n end; begin integer j; A[i]:=A[i]+j+n end; begin integer i, n; n:=n+1; begin integer j; A[i]:=A[i]+j+n end; … end; begin integer i, m; m:=m+1; begin integer j; A[i]:=A[i]+j+n end; … end; ①手続きp本体の 仮引数xを実引数 A[i]で置換 ②iが衝突するの で局所変数をj に付け替え ③呼出し箇所に手 続きを挿入 ④ 手続き内の大域 変数 n が呼出し 側の局所変数と 衝突するので、 名前の付け替え引数渡しと評価方式
引数渡しの方式は式の評価方式と関係する
例
p(X,Y,Z)begin if X>0 then Y else Z end;
において、呼出し
p(f(x), g(x), h(x))
を考える
参照渡しでは → こうした呼出しはできない
値渡しでは → f(x), g(x), h(x)を計算し、その値を渡す
名前渡しでは → f(x), g(x), h(x)をそのまま式として渡し、
27
今日の講義
手続きとは
手続きの定義
引数渡し
スコープ規則
コード(code)
データ(data)
Procedures
Control Flow
Statements
Data Access
Global Static Variables
Global Dynamic Data
Local Variables
Temporaries
Parameter Passing
Read-only Data
プログラミング言語の構成要素
プログラムのメモリレイアウト
各プログラムに割り当てられるメモリの領
域は以下のようになっている
0x0000番地 0xffff番地 プログラム領域 静的領域 スタック領域 ヒープ領域 書き換え不可能 領域が増減する 1プログラムに 割り当てられる 領域 29各領域の説明
プログラム領域 (program)
プログラムの実行コード(機械語)が格納される
静的領域 (static)
プログラムが持つ定数などが格納される
char *a = “This is a string.”; printf(“Hello World!!”);
ヒープ領域 (heap)
大域変数が格納される
スタック領域 (stack)
自動変数
関数呼出し, ブロック開始時に生成される
関数からの復帰, ブロック終了時に消滅する
スタック領域上に確保される
main () { int i, j; func(); } func() { int x, y; } mainの中でのみ使用可能 mainを抜けると消滅 funcの中でのみ使用可能 funcを抜けると消滅 31静的変数
自動変数と同様,宣言した関数内でしか見れ
ない
関数からの復帰時に消滅せず値が保持される
ヒープ領域に確保される
main () { int i, j; tick(); tick(); } tick() {static int count = 0; count++;
funcの中でのみ使用 可能次回呼出し時に 値が残っている 静的変数であることを示す記憶指定子
大域変数(外部変数)
関数の外で宣言する
複数の関数で値を共有できる
プログラム実行中消滅しない
ヒープ領域に確保される
int i, j; main () { i = 1; j = 2; func(); } func() { printf(“[%d,%d]”, i, j); } mainでもfuncでも使用可能 消滅しない [1,2]が表示される 33有効範囲 (スコープ, scope)
名前(変数名、手続き名等)とその束
縛に対し、その名前でその束縛が参照
されるとき、その名前は
可視 (visible)
と
いう
ある名前が可視であるプログラムの範
囲を
スコープ(scope)
という
35
有効範囲 (スコープ)
コープ規則:
1.
ある手続きを実行中に変数
x
が表れたとき、
x
の宣言がその手続きにあるならば、この宣
言が
x
の束縛を決める
2.
その手続きにないなら、この手続きを定義し
ている手続き(親手続き)の中に宣言を探す。
そこにあれば、その宣言が束縛を決める
3.
さらに、そこになければ…その親…
有効範囲 (スコープ)
通常のスコープの規則
静的スコープ規則 (static scope rule)
静的スコープ規則は、プログラムによって
予め決められる規則である
プログラムの字面で決まるので、
lexical scope
rule
ともいう
逆に、プログラムのある箇所での束縛の全
体を
環境(environment)
という
変数のスコープと寿命
スコープ: その変数を見れる範囲
寿命: その変数に値が保持されている期間
37スコープ
寿命
自動変数
関数内
プログラムの全実行
静的変数
関数内
プログラムの全実行
大域変数
プログラム全体
プログラムの全実行
スコープと可視性
別々のスコープで同じ名前を使用できる
名前が衝突したときは,内側のスコープを優先
int i, j; main () { func1(); func2(); } func1() { int i, j; } func2() { int i, j; } main内における i , j func1内における i , j func2内における i , j39