情報処理演習 (2)C言語の入門

(5)関数 その1
システム科学領域 日浦 慎作
関数とは
• 一連の仕事をひとつにまとめたもの
引数
• 簡単な指示で複
雑な仕事をさせる
ことができる
• 指示を出すほう
は作業方法を知
らなくてもよい
• あちこちから何度
でも同じ仕事を依
頼できる
• 少し違った要求な
らこたえることが
出来る
牛丼並ひとつ
戻り値
牛丼関数
はいよっ
280円
牛丼,大盛り,
つゆだく
ねぎだく
はいよっw
400円
仕事:
・ご飯を
丼に盛る
・肉・玉葱・
つゆを入れる
・客に出す
・金を取る
関数
関数の例
• 整数を自乗する関数
#include <stdio.h>
int square (int x);
関数の宣言(関数の概要の記述)
int main(void) {
int y;
y = square(5);
printf(“result is %d\n”, y);
return 0;
}
int square (int x) {
return x * x;
}
関数の定義
(関数そのもの記述)
関数の「いいところ」
• 関数の中身を知らなくていい
– 「どういう結果を生じるか」のみ知っていればOK
– プログラムが見やすく,分かりやすくなる
• 同じ処理を何度も書かなくて良い
– 同じ処理をするたびに,呼び出せばよい
• 処理を「一人立ち」させることが出来る
– 関数を独立に作成・デバッグし,
完成したものとして提供できる
– 他人様にも簡単に使っていただける
関数の「精神」
• ブラックボックス化
– 関数の中身は必ずしも知らなくてよい
– 処理は複雑でも,提供する機能は分かりやすい
• 例えば,sin( ) とか sqrt( ) など
– 確実に動作する
• バグ(不具合)がよく除かれている
– 明示的な結果以外の作用(副作用)がない
• 他の変数の値が勝手に変わるなど
関数の「仕様」
• どんな「引数」を取るのか
– 引数の数,型 (int, double, ..)
• どんな「戻り値」を返すのか
– 戻り値の有無,戻り値の型
– 戻り値無しの場合は void を使う
• 関数の機能
– どのような引数を与えるとどのように動作し,
どのような結果(画面入出力,戻り値など)を
もたらすのか
関数の使い方
• どこからでも呼び出すことが出来る
y = sq (i);
普通の式
printf(“val = %d\n”, sq(j) ); 関数の引数で
z = sq( sq (2) );
(同上)
• 戻り値を利用することも,使わないのもOK
• 引数の型に注意 (int に double を入れる等)
関数の中身
int max (int a, int b);
int max (int a, int b) {
int result;
戻り値の型
}
if(a > b) {
result = a;
}
else {
result = b;
}
return result;
関数の宣言(関数の概要の記述)
引数の名前と型
関数内部で変数を定義できる
(有効範囲はこの関数内だけ)
関数の定義
(関数そのもの記述)
return で関数を抜けることが出来る
その際に戻り値を指定できる
関数の注意事項
• 関数内で変数を定義したり値を変更しても,
呼び出し側には影響しない
(テキスト ポイント8)
• 呼び出し側に影響を与える3つの方法
– 戻り値で情報を戻す
– ポインタを使う(次回講義で説明)
– 「配列」を使う
再帰呼び出し
int frac(int n) {
int r;
if(n == 1)
r = 1;
else
r = n * frac(n-1);
return r;
}
• 関数は自分自身を呼
び出すことが出来る
再帰呼び出し
120
int frac(int n) {
int r;
if(n == 1)
r = 1;
else
r = n * frac(n-1);
return r;
}
frac(5)
frac(4)
frac(3)
frac(2)
frac(5-1)
frac(4-1)
frac(3-1)
frac(2-1)
24
6
2
frac(1)
1
• 呼び出した関数の状態は保持されている
• どこかで再起呼び出しをやめて return しなけ
ればならない
(6)配列 その1
システム科学領域 日浦 慎作
配列とは
• 同じ形式のデータを並べたもの
• 名前で指定するのではなく,番号で指定
番号
0
1
2
3
4
5
6
データ
85
72
48
96
84
77
58
#define NUM 30
int main(void) {
int i;
int main(void)
{
int
intseiseki[30];
i;
int seiseki[NUM];
seiseki[0] = 10;
seiseki[1]
seiseki[0]==20;
10;
seiseki[1] = 20;
for (i = 0; i < 30; i++) {
seiseki[i]);
for (i =printf(“%d\n”,
0; i < NUM; i++)
{
}
printf(“%d\n”, seiseki[i]);
…..
}
…..
C言語での配列
• 0から始まる
–
–
–
–
1から始まる言語もある
int data[10] とした場合,使える添え字は 0~9
添え字は整数値しか使えない (double 使用不可)
添え字に変数や数式を使うことが出来る
• 要素数は固定
– 途中で増やしたり減らしたり出来ない
– 添え字が要素数を超えているかどうかはチェックされない
(超えてしまうとプログラムが異常動作する)
• どのような型でも配列に出来る
– int, double, その他なんでも可能
初期値の入れ方
番号
0
1
2
3
4
5
6
データ
85
72
48
96
84
77
58
int seiseki[7] = {85, 72, 48, 96,
84, 77, 58};
もしくは
int seiseki[] = {85, 72, 48, 96,
84, 77, 58};
• 添え字を省略することも出来る
• 初期値は要素より少なくても良い
二次元配列
• 二次元配列は以下のように書く
int data[5][10];
(int data[5,10]; は間違い)
• 3次元以上も可能
• 初期化も可能
– int data[4][3] = {
{5, 6, 3},
{4, 5, 2},
{6, 3, 1},
{1, 8, 9} };
配列を使うと
• 以下の課題などは簡単に実装できる
• 数量の制限を外すことが出来る
– 課題 5人の身長を入力すれば,その平均身長を
計算するプログラムを作成せよ.
→ n 人の身長を入力すれば..
– 課題 4つの整数値を読み込んで,その最大値と
最小値を求めるプログラ ムを作成せよ.
→ n 個の整数値を読み込んで..
など.
(7)関数その2
配列を引数に取る関数
システム科学領域 日浦 慎作
配列を受け取る関
数であることを宣言
する
配列を引数に渡す
int minimum(int data[], int num);
int main(void) {
int seiseki[10] = {70, 85, … };
int min;
呼び出し側では,
配列の名前のみ指
定すればよい
min = minimum(seiseki, 10);
printf(“minimum is %d\n”, min);
}
int minimum(int data[], int num) {
…..
受け取り側では,
}
配列の要素数を指
定する必要は無い
配列の中身の変更
int change(int data);
int change(int data[], int num);
int main(void) {
int d = 70;
int main(void) {
int d [10] = {70, 85, … };
change (d);
printf(“d is %d\n”, d);
change (d, 10);
printf(“d[0] is %d\n”, d[0]);
}
}
int change(int data) {
data = 0;
}
int change(int data[], int num) {
data[0] = 0;
}
値は変わらない
値が変わる
const 修飾子
• const は値の変更を禁じる
• 変数を const にした場合:値の変更が不可能
const int a = 10;
a = 20;  エラー
• 引数を const にした場合:値が変更されない
int func (const int data[], int n);
とすると,func 内で data に値を代入できない
int func (const int data[], int n) {
data[0] = 10;  エラー
}
引数を関数に渡す仕組み(1)
int change(int data);
int main(void) {
int d = 70;
実行位置
change (d);
printf(“d is %d\n”, d);
}
int change(int data) {
data = 0;
}
70
d
コピーが起こる
0
値渡し
data
引数を関数に渡す仕組み(2)
データが格納される領域は共通
int change(int data[], int num);
int main(void) {
実行位置 int d [7] = {70, 85, … };
change (d, 7);
printf(“d[0] is %d\n”, d[0]);
}
int change(int data[], int num) {
data[0] = 0;
}
0
d
data
1
70
0
85
2
…
3
4
5
6
要素数は
いくらでも良い
二次元配列の引数渡し
• 二次元配列の場合は,引数の個数を指定す
る必要がある
– int func(int data[][]);  エラー
– int func(int data[2][3]);  OK
– int func(int data[][3]);  OK
– int func(int data[2][]);  エラー
二次元配列
int change(int data[][3]);
int main(void) {
int d [2][3] = {{1, 2, 3},
{4, 5, 6}};
d
data
change (d);
printf(“d[1][2] is %d\n”,
d[1][2]);
}
[0][0]
1
[0][1]
2
[0][2]
3
[1][0]
4
[1][1]
5
[1][2]
06
要素数は
いくらでも良い
int change(int data[][3]) {
data[1][2] = 0;
(C言語では
}
チェックされない)
data[a][b] は,a * 3 + b 番の要素(この計算を実行時に行うために3が要る)