プログラミング入門 第13回講義 関数(その1) 関数とは(3) 関数の呼出と定義(7) 引数と仮引数(10) 戻り値(12) プロトタイプ宣言(13) 関数の例(19) マークのあるサンプルプログラムは /home/course/prog0/public_html/2013/lec/source/ 下に置いてありますから、各自自分のディレクトリに コピーして、コンパイル・実行してみてください Prog-0 2013 Lec13-1 Copyright (C) 1999 – 2013 by Programming-0 Group 長いプログラムを分割 プログラムが長く、 複雑になってくると • 処理の流れや詳細を一覧して 理解するのは大変 • 同じような処理を何回も書か なくてはならない • 修正や改良が困難 長いプログラムを 小さな部品に分 割すると • プログラムが理解しやすくなる • 同じ部品を繰り返して使える • 異なるプログラムにも、部品の 再利用が可能 • 修正や改良がやりやすい 1 Prog-0 2013 Lec13-2 Copyright (C) 1999 – 2013 by Programming-0 Group 関数とは(p.162) 何度も使用できるプログラムのまとまり。 与えられた情報を基に結果を計算する。 関数には標準で用意されているものもある。 これまでに出てきたものとしては: 数学関数(詳しくはp.127参照) sin, cos, sqrt(二乗根) 等 標準入出力関数 scanf, printf 等 Prog-0 2013 Lec13-3 Copyright (C) 1999 – 2013 by Programming-0 Group 2乗・4乗・8乗の計算例 /* 2,4,8乗を計算する version 1 */ main() { double a = 3.0, a2, a4, a8; a2 = a*a; a4 = a*a*a*a; a8 = a*a*a*a*a*a*a*a; printf("a2:%f a4:%f a8:%f\n", a2, a4, a8); } /* 2,4,8乗を計算する version 2 */ main() { double a = 3.0, a2, a4, a8; a2 = a*a; a4 = a2*a2; a8 = a4*a4; printf("a2:%f a4:%f a8:%f\n", a2, a4, a8); } Prog-0 2013 Lec13-4 計算に無駄が多い 2乗の計算の繰り返し 関数を利用できる Copyright (C) 1999 – 2013 by Programming-0 Group 関数を利用して書き換えると #include <stdio.h> double nijou(double); main() { double a2, a4, a8; a2 = nijou(3.0); /*値*/ a4 = nijou(a2); /*変数*/ a8 = nijou(a2*a2); /*式*/ printf("a2:%f a4:%f a8:%f\n", a2, a4, a8); } 実行例: std1dc1{s1000000}1: ./a.out a2:9.000000 a4:81.000000 a8:6561.000000 std1dc1{s1000000}2: double nijou(double x) { 2乗の計算を関数に! double y; mainから3回呼ばれる y = x * x; return y ; } /home/course/prog0/public_html/2013/lec/source/lec13-1.c Prog-0 2013 Lec13-5 Copyright (C) 1999 – 2013 by Programming-0 Group 関数概略(p.166) プロトタイプ宣言: lec13-13 #include <stdio.h> double nijou(double); main() { .. 関数の呼び出し:lec13-7 引数:lec13-10,11 a4 = nijou(a2); .. 仮引数:lec13-10,11 } double nijou(double x) { double y; y = x * x; return y ; } Prog-0 2013 Lec13-6 標準的なプログラ ムの構成: ① インクルード ② マクロ定義 ③ プロトタイプ ④ メイン ⑤ 関数 関数の定義:lec13-8,9 戻り値:lec13-12 Copyright (C) 1999 – 2013 by Programming-0 Group 関数の呼び出し(p.172) 関数はmain又は ほかの関数の中に 呼ぶ関数の名前 を書くことで、呼び出せる。 .. main() { .. a2 = nijou(x); .. } この時のデータのやり取 りとして戻り値(もどりち) と 引数(ひきすう) がある (次に説明する) double nijou(double x) { .... } 2 Prog-0 2013 Lec13-7 Copyright (C) 1999 – 2013 by Programming-0 Group 関数の定義(1) 関数の実際の中身を記述する 関数との値の受け渡しには戻り値と仮引数を使用する 関数中の書き方は、 main()と同じ 3 double nijou(double x) { double result; result = x * x; return result ; } Prog-0 2013 Lec13-8 Copyright (C) 1999 – 2013 by Programming-0 Group 関数の定義(2) 関数内で宣言される変数と仮引数 は自動(ローカル)変数である (必要な場合)戻り値は関数の型に 変換される double nijou(double x) { double result; result = x * x; return result ; } [自動(ローカル)変数] これまで使ってきた普通の変数のこと 関数内で宣言された変数は、その関数内でのみ有効 その関数が呼ばれる度に領域が確保される。 暗黙に初期化されない(初期化する必要がある) 詳しくはlec13-21にて説明する Prog-0 2013 Lec13-9 Copyright (C) 1999 – 2013 by Programming-0 Group 引数、仮引数(1)(p.180) 引数:関数を呼び出す側で使われる変数。関数に渡され る情報で、次に挙げる仮引数と対応する。 仮引数:関数内で用いられる変数で、呼び出し側から渡さ れる情報。呼び出し側の引数に対応する。 複数ある場合は、カンマ区切りで型と名前を列挙する。 引数には、定数、変数、式いずれも使用可能。 main() { double a,b; ... b = mult(a, b); ... 引数 } Prog-0 2013 Lec13-10 double mult(double x, double y) { 仮引数 x = x * y; return x ; } Copyright (C) 1999 – 2013 by Programming-0 Group 引数、仮引数(2) 順序と型だけに意味があり、引数と仮引数で同じ名前 である必要はない。 情報は引数から仮引数に コピー されて、関数内 で使用される。関数内で仮引数の値が変わっても、 引数に書かれた定数・変数・式の指す値は変わらない。 引数がない場合は 「void」 型とする(→lec13-2.c) 4 main() { double a,b; 値コピー ... b = mult(a, b); ... } Prog-0 2013 Lec13-11 double mult(double x, double y) { x = x * y; xが変わって return x ; も元のaは変 わらない! } Copyright (C) 1999 – 2013 by Programming-0 Group 関数の戻り値(もどりち) 関数の定義で指定した型(関数の型)の値を返す 戻す値を 「return 文」 で指定する 具体的には次のように書く return 値; 値(あたい)の表現には定数、変数、式、いずれも使える。 それらが計算された結果の値(あたい)が戻り値となる。 return 文で戻せる値は一つだけ 「戻り値」が関数の値になる 値を戻さない場合は型を 「 void 」にする(→lec13-2.c) (定型文を出力処理するだけの関数など) 5 Prog-0 2013 Lec13-12 Copyright (C) 1999 – 2013 by Programming-0 Group プロトタイプ宣言(p.168) 使用する関数の 名前や型、 引数の順序と型 を、 プログラムの最初に宣言する 最後がセミコロン";"で終わっていなければならない 6 プロトタイプ宣言を書かない書き方も可能(本によってはそうい う形式の物もある)だが → 最新のCの規格(C99)では必ず 書くようになっているので、書くよう習慣づけよう! 引数の名前は書かなくてもよい(型のみを記述) double func1(double, int); Prog-0 2013 Lec13-13 Copyright (C) 1999 – 2013 by Programming-0 Group #include <stdio.h> /* 2乗を計算する関数 */ double nijou(double); main() { double a = 1.73 , b , c ; b = nijou(a); c = nijou(1.41); ...(以下略) 1回目 } 2回目 1回目 2回目 double nijou( double x ) { double y; y = x * x; return y ; } Prog-0 2013 Lec13-14 関数の注意1 仮引数には引数の値が コピーされる 一度目:xはa (つまり1.73) 二度目:xは1.41 戻り値が関数の値になる 一度目:nijou(a)は約3.0 二度目:nijou(a)は約2.0 Copyright (C) 1999 – 2013 by Programming-0 Group #include <stdio.h> /* 平均を計算する関数 */ double ave(int, int); main() { int a = 2 , b = 3; double average; average = ave(a , b); ... } double ave(int x, int y) { double z; z = (x + y)/ 2.0; return z ; } Prog-0 2013 Lec13-15 関数の注意2 プロトタイプと関数本体の宣言が 一致していないといけない! 引数の型、個数、順番 プロトタイプ宣言には変数名は必要 ない 戻り値の型 名前 例えば、戻り値の型、引数の型・数 などが一致していないと 「conflicting types for ‘関数名’」 と言うようなメッセージが出てコン パイルエラーとなる Copyright (C) 1999 – 2013 by Programming-0 Group #include <stdio.h> double nijou(double); main() { double a = 1.73 , b , c ; b = nijou(a); c = nijou(1.41); 1回目...(以下略) } 2回目 double nijou( double x ) { double y; y = x * x; return y ; } Prog-0 2013 Lec13-16 関数の注意3 戻り値が関数の値になる nijou(a)の値: 一度目:約3.0 二度目:約2.0 戻り値の型は宣言時の型 左の例の戻り値は double型 7 戻り値がない場合はvoid型 関数宣言でfunc(int); のように戻り値を省略す ると、戻り値はint型(int func(int);)だと自 動的に解釈されるので、戻り値がない場合は必ず voidを付けて、void func(int);のように宣言 する必要がある。 Copyright (C) 1999 – 2013 by Programming-0 Group #include <stdio.h> 関数の動作おさらい(p.165) double nijou(double); main() { double x = 2.0, a1, a2, a3; a1 = nijou(3.0); /*値*/ a2 = nijou(x); /*変数*/ a3 = nijou(x * 2.0); /*式*/ printf("a1:%f a2:%f a3:%f\n", a1, a2, a3); } double nijou(double x) { double y; y = x * x; return y ; } Prog-0 2013 Lec13-17 1. 2. 3. mainの最初の実行文から開始 nijou(3.0)が評価される 3.0が関数の仮引数xにコピーされ、関数 nijouに実行が移る 4. 計算の結果、yに9.0が入り、「return y」で 9.0がnijou関数の結果となる 5. nijou関数の結果がa1に代入される。 6. 次にnijou(x)が評価される 7. x(2.0)が関数のxにコピーされ、関数nijouに 実行が移る 8. 計算の結果yに4.0が入り、「return y」で4.0 がnijou関数の結果となる 9. nijou関数の結果がa2に代入される 10. a3も同様に計算される 11. printfでa1,a2,a3が表示される 12. 終了 Copyright (C) 1999 – 2013 by Programming-0 Group 関数から関数を呼ぶ(p.184) 関数は必ずしも main から呼ばれるとは 限らない。 関数から呼ばれることもある。 8 main a b main c mainから関数a,b,c を呼ぶ Prog-0 2013 Lec13-18 a b c mainが関数aを呼び、 aがbを、bがcを、 順に呼んでいく Copyright (C) 1999 – 2013 by Programming-0 Group いろいろな関数の例(1) /* 円の面積を計算する */ double circle0(double r) { double area; area = 3.14*r*r; return area; } /* nijou()を使って円の面積を計算する */ double circle1(double r) { double area; area = 3.14*nijou(r); return area; } /* 円柱の体積を計算する */ double cylinder(double r, double h) { double volume; volume = circle1(r)*h; return volume; } /* ボディマス指数(BMI)を計算する t : 身長 (cm) w : 体重 (kg) */ double bmi(double t, double w) { double t_meter; t_meter = t/100.0; /* m単位に変換 */ return w/nijou(t_meter); } /home/course/prog0/public_html/2013/lec/source/lec13-{3a,3b,3c,3d}.c Prog-0 2013 Lec13-19 Copyright (C) 1999 – 2013 by Programming-0 Group いろいろな関数の例(2) ベクトルの内積の定義 a b | a || b | cos /* 平面上の二つのベクトルの角度の余弦(コサイン)を計算する x1, y1 :ベクトル1 x2, y2 :ベクトル2 */ double vcos(double x1, double y1, double x2, double y2) { double dot, c; dot = x1*x2 + y1*y2; return dot/(vlen(x1,y1)*vlen(x2,y2)); } /* ベクトルの大きさを計算する * / double vlen(double x, double y) { return sqrt(x*x + y*y); /* sqrt()は平方根を計算する関数 */ } /home/course/prog0/public_html/2013/lec/source/lec13-4.c Prog-0 2013 Lec13-20 Copyright (C) 1999 – 2013 by Programming-0 Group 自動(ローカル)変数 =普段使う変数(p.189) #include <stdio.h> 通用範囲 void a(void); void b(void); main() { int i = 1; 関数内のみで通用 関数が呼ばれると 生成 され、 リターン すると消滅する。 別関数なら(通用範囲が違うので) 同じ名前 でも構わない 9 main()のi a(); b(); 10 11 } b()のi a()のi 時間 main()のi 実 行 終 了 main 実 行 開 始 b() 実 行 終 了 b() 実 行 開 始 a() 実 行 開 始 a() main Prog-0 2013 Lec13-21 b ()のi void b(void) { int i = 3; printf("i : %d\n",i); } 通用期間 a ()のi void a(void) { int i = 2; printf("i : %d\n",i); } 実 行 終 了 /home/course/prog0/public_html/2013/lec/source/lec13-5.c Copyright (C) 1999 – 2013 by Programming-0 Group printfやscanfも関数です(ライブラリ関数) printf("%d",i);の場合、"%d"とiが引数になる。 戻り値の例: scanf /usr/include/stdio.hで 整数型の戻り値を持つ 定義されているマクロ 正常に入力された変数の数を返す ファイルの終端を読みこむと、EOF(-1)を返す あらかじめ定義されているのでプロトタイプ宣言が不要 → stdio.hに記述されている (/usr/include/stdio.h を参照のこと) Solarisでは /usr/include/stdio.h内で更にincludeされている /usr/include/iso/stdio_iso.h に記述されている。 Prog-0 2013 Lec13-22 Copyright (C) 1999 – 2013 by Programming-0 Group scanfの戻り値を利用した プログラム #include <stdio.h> main() { int data[100],result,i,n; for(i = 0 ; i < 100 ; i++){ /* data 入力 */ result = scanf("%d",&data[i]); if (result == 1) continue; /* scanf 成功 */ else if (result == EOF) break;/* 入力終了 */ else{ /* scanf 失敗 */ printf("Input data error!\n"); break; } } n = i; /* i に正常に入力された個数が入っている */ for(i = 0 ; i < n ; i++){ /* データ出力 */ printf("data[%2d] : %d\n",i,data[i]); } } 実行結果 std1dc1{s1000000}1: ./a.out 1 2 3 4 (改行) Control+D data[ 0] : 1 data[ 1] : 2 data[ 2] : 3 data[ 3] : 4 std1dc1{s1000000}2: ./a.out 1 2 3 k 4 5 (改行) Input data error! data[ 0] : 1 data[ 1] : 2 data[ 2] : 3 std1dc1{s1000000}3: /home/course/prog0/public_html/2013/lec/source/lec13-6.c Prog-0 2013 Lec13-23 Copyright (C) 1999 – 2013 by Programming-0 Group 授業・演習で使用されるライブラリ関数 <stdio.h> でincludeされる関数 printf、scanf、 fprintf(stderr...)(エラー出力) <stdlib.h> でincludeされる関数 exit(強制終了)、rand(乱数発生) <math.h> でincludeされる関数 sqrt(二乗根)、fabs(絶対値)、pow(べき乗) Prog-0 2013 Lec13-24 Copyright (C) 1999 – 2013 by Programming-0 Group
© Copyright 2024 ExpyDoc