プログラミング演習

プログラミング演習
初級編
2004年7月15日(木)
奈良先端科学技術大学院大学
情報科学研究科
猿渡 洋 斎藤 将人 新保 仁
本日の内容
I.
II.
III.
関数の基礎:宣言と呼び出し
変数の有効範囲と引き渡し
再帰呼び出し
I. 関数宣言と関数の
呼び出し
関数とは

y = f(x)
 関数は、名前を持つ(関数名:f)
 関数へ引き渡す変数(引数:x)
 複数あることも f(x1, x2), f(x1, x2, x3), …
 全くないこともある: f()


関数からの戻り値 (ないこともある)
計算や処理を、機能ごとにまとめたもの
関数の基本形

関数の宣言
 関数名
 引数(関数に与える値)の個数と型
 戻り値の型
 戻り値を持たない関数の場合は void 型

内部処理の記述
 これまでの main() と同様でよいが,
 void 型以外の関数は,必ず “return 値;” で、戻り
値を指定して関数を終了する。
関数の「宣言」
#include <stdio.h>
戻り値の型(戻り値が無い関数の場合 void)
int square(int x)
{
return x * x;
}
仮引数:実行時に与えられる引数を
仮に x として処理を書く
ここの値が戻り値となる
(次ページへ続く)
関数の呼び出し
(前ページからの続き)
int main()
実は main() も int 型の関数.
{
int a, b;
printf("Type any integer.\n");
scanf("%d", &a);
a:実引数
b = square(a);
この値を引数とし関数を呼び出し
printf("%d\n", b);
return 0;
}
main()関数の戻り値
処理の流れ
main()
a に値を代入
b = square(a);
square()
実引数 a として
呼び出し
square(a)を計算
bに代入
x * x を計算
戻り値
出力
仮引数 x で受ける
関数を使うメリット



同じ処理を何度も書く必要がなくなる
同じ処理を他のプログラムでも再利用しやすい
プログラムを見やすくできる
プログラミングの心得
プログラムは、可能なかぎり
独立な最小単位に分離して
個々に関数化するよう
こころがけよう!
関数例:階乗計算
int factorial(int n)
{
その関数の中だけで使用する変数は,そ
の関数の中で宣言する (ローカル変数).
int i, x;
x = 1;
for (i=2; i<=n; i++) {
x = x * i;
}
return x;
}
関数の型宣言

関数は呼び出しより前に宣言する.
 factorial() 関数の宣言が main() より後にあると
コンパイルエラーとなる.

関数の呼び出し型 (戻り値および引数の型,数) だけをあら
かじめ宣言しておき、実際の関数はあとで書く.
 int factorial(int n);
をファイルの先頭に書いておくとそれ以降, 使えるようになる.
演習課題
演習5-1 n 個の要素から r 個の組を取り出す際の組み合わせ
の数 nCr を計算する関数 combination を作成し,
その動作を確認せよ.
(ヒント:複数の引数はコンマで区切って宣言・呼び出
しする)
int combination(int n, int r);
II.
変数の引き渡しと
有効範囲
関数間のデータの授受
1.
2.
3.
値渡し
ポインタによるアドレス渡し
外部変数渡し
1. 値渡しの例: square 関数
int square(int x)
{
return x * x;
}
int main()
{
int a, b;
…
b = square(a);
…
}
1.値渡し



実引数の値を関数側の仮引数にコピー
呼ばれた関数内で仮引数の値を変えても元の実引数に影
響しない
Cの標準的な引数の渡し方
square()
main()
int a;
b = square(a);
a の値を
引き渡す
仮引数 x で
受ける
値渡しの例
#include <stdio.h>
void display(int n)
{
n = 2;
printf(“In 'display' function, n=%d\n”, n);
}
int main()
{
こちらの n の値は
int n = 1;
変化する?
display(n);
printf(“In 'main' function, n=%d\n”, n);
return 0;
}
2.アドレス渡し


実引数の値ではなくそのポインタを渡す
関数内ではポインタを使って実引数のアドレスを直接参照
main()
int a;
実引数 a の
アドレスを
引き渡す
b=square(&a);
元の実引数 a を
ポインタで
直接参照できる
square()
*x で a の値を
直接参照
アドレス渡しの例
#include <stdio.h>
void init(int x[], int *y)
{
int i:
for(i=0;i<100;i++) x[i] = i;
*y = i;
}
配列 x[] とポインタ y が指し
示すアドレスに値を書き込む
int main()
{
int a[100], n = 0;
init(a, &n);
printf("n=%d\n", n);
while (n-- > 0) printf("%d:%d\n", n, a[n]);
}
アドレス渡しの使い方

関数から2つ以上の値を返したいとき
呼ばれた関数内で元関数の値を変えたいとき
例: scanf("%d", &i) scanf内で i の値を変更

配列を引き渡すとき

3.外部変数渡し


引数を用いない変数の引渡し法
関数の外で外部 (グローバル) 変数を定義して、そこを介し
て値をやりとりする。
外部引渡しの例
#include <stdio.h>
int a[100];
配列 a[] をグローバル変数として定義
int init()
{
int i:
for(i=0; i<100; i++) a[i] = i;
return i;
}
int main()
{
外部変数a[]を直接参照
int n;
n = init();
printf(“n=%d\n”, n);
while (n-- > 0) printf(“%d:%d\n”, n, a[n]);
return 0;
}
変数の有効範囲(スコープ)
内部変数(ローカル変数)
 ある関数内で宣言された変数
 その関数内でのみ参照可能
 関数が終わると消滅
 他の関数からは見えない。独立。
外部変数(グローバル変数)



関数の外で宣言された変数
以降の全関数から参照できる。
関数終了以後も値が残る。
演習問題
演習5-2 演習4-1 の商品一覧表示プログラムを関数を使って書き直せ. 商
品リスト (商品名, 単価からなる構造体の配列) は固定ではなく,
呼び出し側で指定できるようにすること.
ヒント: 構造体配列(商品リスト)へのポインタと,配列の要素数
(商品数)を引数とするとよい.
以下の二つの商品リスト a, b それぞれに対して, 関数の動作を確
認せよ.
商品リスト a (商品数 3)
商品リスト b (商品数 2)
ID
商品名
単価(円)
ID
商品名
単価(円)
0
Pencil
60
0
Toothbrush
200
1
Eraser
50
1
Toothpaste
350
2
Ruler
120
III. 再帰呼び出し
階乗計算:再帰呼び出しを使って
int factorial2(int n)
{
自分自身を呼び出し
int r;
if (n > 1) {
r = n * factorial2(n - 1);
} else {
r = 1;
}
return r;
n が 1 以下ならそれ以上は呼び出さない
}
変数の有効範囲(スコープ)
内部変数(ローカル変数)
 ある関数内で宣言された変数
 その関数内でのみ参照可能
 関数の実行が終わると消滅
 他の関数(再帰的に呼ばれた同じ関数も含む)からは見
えない。独立。