double nijou( double x )

プログラミング入門
第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