cp-9. 再帰関数
C プログラミング入門
URL: https://www.kkaneko.jp/pro/adp/index.html
1
金子邦彦
局所変数を使った関数の理解、再帰呼び出しによ
漸化式の計算
学習内容の構成
1. スタック:配列とスタックポインタによる
push/pop操作の実現
2. 局所変数と大域変数:関数内部でのみ有効な変
数と、複数関数で共有される変数の区別
3. 再帰関数:自分自身を呼び出す関数による総和
計算
4. 関数呼び出しの流れ:引数の受け渡しと戻り値
の仕組み
前提:C言語の関数、配列の基本
意義:データ構造の実装手法と再帰的思考の習得
2
スタックのプッシュ(push), ポップ(pop) を行う
関数を作る
次の2つの大域変数を使うこと
1. 配列stack
2. スタックポインタtop
3
例題1.スタック
スタックのプッシュとポップ
プッシュ
(入れる操作)
ポップ
(出す操作)
すでに
入っていた
データ
積み重なる
ように入る
すでに
入っていた
データ
一番最後に入った
データが先に出る
4
スタックとは
入れた順に積みあがっていく
1 1
2
3
2
1
2
1
2
1
4
push
push 2 push 3 pop push 4
5
#include <stdio.h>
int stack[100];
int top=0;
void push(int n)
{
stack[top]=n;
top++;
return;
}
int pop()
{
int n;
top--;
n = stack[top];
return n;
}
push関数
pop関数
6
スタック
main 関数の例
int main()
{
push(1);
push(2);
push(3);
printf( "%d¥n", pop() );
push(4);
printf( "%d¥n", pop() );
printf( "%d¥n", pop() );
printf( "%d¥n", pop() );
return 0;
}
7
スタック
実行結果の例
3
4
2
1
8
関数呼び出しの流れ
main 関数
int main()
push(1);
push(2);
push(3);
printf( "%d¥n", pop() );
push(4);
printf( "%d¥n", pop() );
printf( "%d¥n", pop() );
printf( "%d¥n", pop() );
関数呼び出し
戻り
push 関数
void push( int n )
return;
戻り
pop 関数
int pop()
return n;
9
配列によるスタックの実現
スタック
十分に多くのデータを格納できる配列stackを用意.
スタックポインタ
現在の「空き部分の底」を示す値(スタックポイ
ンタ)を格納させる変数topを用意.
スタックの底は stack[0],スタックの頂上は
stack[top-1]
配列
スタックの底 stack[0]
スタックの頂上 stack[top-1]
空き部分の底 stack[top]
10
配列によるスタックの実現
push 時: pushの後にtopの値を1足す
pop 時: popの前に,topの値を1引く
1 1
2
3
2
1
2
1
2
1
4
push
push 2 push 3 pop push 4
1
top
2 3 4 3 4
11
局所変数
関数の仮引数と,関数本体内で宣言された変
数のこと
関数の内部でだけ有効
ほかの関数(main関数など)からは直接アクセス
できない
ある関数の局所変数と,呼び出した関数中の
局所変数と,例え同じ名前であっても,別の
実体である
12
大域変数
関数の外側で宣言された変数のこと
例) 配列stackとスタックポインタtopは,push
関数とpop関数のいずれからも参照できる.
#include <stdio.h>
int stack[100];
int top=0;
int push(int n)
{
stack[top]=n;
top++;
return n;
}
int pop()
{
int n;
top--;
n = stack[top];
return n;
}
これが大域変数
push関数と
pop関数で
共有される
13
局所変数と大域変数
局所変数 大域変数
宣言の場所
関数の仮引数
または関数内
関数の外側
有効範囲
関数の内部だ
けで有効
複数の関数で
共有される
14
スタックの使用例
正整数を読み込んだら,pushする.
負整数を読み込んだら,popして,取り出された値
を表示する.
が読み込まれるまで上の操作を繰り返す.
15
#include <stdio.h>
#pragma warning(disable:4996)
int main()
{
int item;
top= -1;
do {
printf("Enter a number ");
scanf("%d",&item);
if (item>0) {
printf("pushed: %d¥n",item);
push(item);
} else if (item<0) {
printf("popped: %d¥n",pop());
}
} while (item!=0);
return 0;
}
16
課題1.スタック操作の例外処
スタック操作のプログラムにおいて,
push 関数と pop 関数を書き換えて,
次に挙げた,2つの例外事象の対処を考慮しなさい
1.スタックが空のときにポップ(popしない.
スタックが空のときに pop 関数が呼び出されると,「Stack
empty」のエラーメッセージを表示すること
2.スタックが満杯のときにプッシュ(pushしない.
スタックが満杯のときに push 関数が呼びだされると,
Stack full」のエラーメッセージを表示すること
17
課題1のヒント
配列
スタックポインタの
値は 「0」
空のとき 満杯のとき
スタックポインタの
値は,「配列のサイズ」
スタックポインタは,現在の「空き部分の底」の
位置を示す値 「スタック内のデータ数」に等しい
18
例題2.再帰関数による総和
整数Nから,1からNまで総和を求める再帰関数
を作る
例) 5 → 15
再帰関数とは,関数 f の本体にf の呼出しを含むよ
うな関数のこと
+
=
=
=
=
otherwise
1 if1
1
1
1
n
i
n
i
in
n
i
19
再帰関数とは
自分自身を呼び出すような関数のこと
必ず終了条件が成立するように気をつけること
20
#include <stdio.h>
#pragma warning(disable:4996)
int sum(int n)
{
if (n < 1) {
return 0;
}
if (n == 1) {
return 1;
}
else {
return n+sum(n-1);
}
}
int main()
{
int n;
int s;
printf("Enter a number: ");
scanf("%d",&n);
s = sum(n);
printf("sum(%d)=%d¥n",n ,s);
return 0;
}
sum関数
main関数
21
再帰関数による総和
実行結果の例
Enter a number: 2
sum(2)=3
22
関数呼び出しの流れ
main 関数で n = 2 のとき)
main 関数
int main()
sum(n);
関数呼び出し
sum 関数
int sum( int n )
return n + sum(n-1);
関数呼び出しと戻
sum 関数
int sum( int n )
return 1;
戻り
23
n の値の変化
main 関数で n = 2 のとき)
main 関数
int main()
sum(n);
関数呼び出し
sum 関数
int sum( int n )
return n + sum(n-1);
関数呼び出しと戻
return
1;
戻り
n = 2
sum 関数
int sum( int n )
n = 1
1を返す
3を返す
24
再帰関数による総和
n == 1
No
Yes
return 1;
return n + sum(n-1);
25
プログラム実行順
main関数で n=2 のとき)
s = sum( n );
main 関数
int main()
n ==1
return n + sum(n-
1);
No
Yes
return 0;
sum 関数
int sum( int n )
関数
呼び出し
戻り
printf( "Enter a number:" );
scanf( "%d", &n );
return 0;
printf("sum(%d)=%d¥n",n ,s);
n ==1
return n + sum(n-
1);
No
Yes
return 1;
sum 関数
int sum( int n )
関数
呼び出し
戻り
26
データの流れ
s = sum( n );
return n + sum(n-1);
関数
呼び出し
戻り
n の値を,
sum 関数に渡す
④返された値を
s に格納する
②渡された値は,
n」という名前で使う
n-1 の値を,
sum 関数に渡す
⑤値「3」を返す
n
2
プログラム
データ
プログラム
main 関数
int main()
bar関数
int sum( int n )
n
2
データ
1
return1;
プログラム
bar関数
int sum( int n )
n
データ
1
④渡された値は,
n」という名前で使う
関数
呼び出し
⑤値「1」を返す
戻り
27
return n+sum(n-1);
int sum(int n)
{
if (n < 1) {
return 0;
}
if (n==1) {
return 1;
}
else {
return n+sum(n-1);
}
}
呼び手
戻り値の受け取り
引数仮引数にセットする
28