C言語では,関数を多用する.(Fortran やBasic のサブルーチンに相当) C言語のプログラムは,関数で構成されるといっても過言ではない.
これまでは,main関数一つだけを使って(標準で用意されている ライブラリ関数を除いて)プログラムを書いてきた.
この時,main関数がだんだん長くなって,読みづらくなかっただろうか?. また,同じ様な作業を行うのに,同じようにプログラムを書くのが大変に ならなかっただろうか?これらは関数を使うことによって解決できる.
これまでにも,たくさん関数を使っている.例えば,prinfやscanf, 数学的な関数としてsinやcos等である.
関数は,何らかの値(引数)を指定すると,引数に対して何らかの演算 や動作をして値を返して(戻り値)くれる.例えば,cosでは
double a; a=cos(0.0);
とすると,cosは0.0という引数を受け取って1.0という戻り値を返して くれる.C言語では,これらすでに用意されている関数だけでなく, 自分で関数を作ることができる.というより,自分で関数を作ることで プログラムを構成していく.
関数は次のような形をとる
戻り値の型 関数名 (引数1の型 引数1, 引数2の型 引数2,.......)
先に挙げたcos関数では,ただ一つのdouble(実数)型の引数をもち,戻 り値もdouble(実数)である.
C言語では,自分で関数を作ることができる.しかし,作った関数を使 う前には関数を宣言(プロトタイプ宣言)しておく必要がある. (省略できる場合もあるが,ここでは触れない.)
scanfやprintfなど,ライブラリ関数(標準で用意されている関数)も 使用する前に宣言する必要がある. これまで,#include <stdio.h> とおまじないを書いてきた. この#includeというのは,ファイルを読み込めという意味で,< >で 囲まれているファイルを読み込む. また***.hというファイルは,ライブラリ関数のヘッダファイルと呼ば れこの中でライブラリ関数が宣言されている. 従って,自分が使うライブラリ関数を含むヘッダファイルを #includeする必要がある. scanfやprintfはstdio.hというファイルの中で宣言されていて, sinやcosの数学関数はmath.hというファイルの中で宣言されている.
なお,Cでは,標準で用意されている関数以外にも,自分でヘッダファイルを 作ることができるが,本演習の範囲外なのでここでは触れない.
戻り値の型 関数名(引数1の型 引数1, 引数2の型 引数2,........); |
(最後のセミコロンを忘れないように)
通常,関数の宣言を#includeや#defineの直後に書く.
プロトタイプ宣言した後,その関数を使用したプログラムを書く
ことができる.しかし,当たり前だが関数の実体がどこかになけ
ればならない.
関数の実体は次のような形を書くことで作られる(この形を mainの外側に作る)
戻り値の型 関数名 (引数1の型 引数1, 引数2の型 引数2,.......) { 関数が行う作業; return 戻り値; } |
(プロトタイプ宣言と違って,セミコロンがないことに注意)
この形をみると何かに似ていることに気づくかもしれない.そう
mainである.
C言語では,mainというのは主プログラムの関数名で,このmain関 数が実際に実行される. そのほかの関数は,main関数から呼び出される形となる.
/* * 二つのint型整数の小さい方の値を返す関数 * 関数定義と関数呼び出し,引数,返却値型などの基礎 */ #include <stdio.h> int min(int x, int y); /*関数 min のプロトタイプ宣言 */ int main(void) { int m, n; printf("整数M:"); scanf("%d", &m); printf("整数N:"); scanf("%d", &n); printf ("小さい方の値は%dです.\n", min(m, n)); return 0; } int min(int x, int y) { if (x < y){ return x; }else{ return y; } } |
[解説]
int min(int x, int y); |
この部分は,プロトタイプ宣言である. 整数型の引数を2つ入力して,整数型の戻り値を返すプログラム だということが分かる.
printf ("小さい方の値は%dです.\n", min(m,n)); |
ここで,関数を使っている. 関数minの戻り値は整数型なので,printfでは %d を使って整数値 を表示している.
int min(int x, int y) { if (x < y){ return x; }else{ return y; } } |
この部分が,関数minの実体である.もちろん,プロトタイプ宣言 と形が一致していなくてはならない. この関数は,大小関係を比較する関数です.if文によって x < y の時にはx の値を戻り値として返し,その他の時には y の値を 戻り値として返している( if 文やfor文などで,括弧が必要とさ れないことについては,ここを 参照).
プロトタイプ宣言や関数の実体のところで,戻り値の形を整数型 としているので,必ず整数型の数値を"return" によって返さなけ ればならない.
#include <stdio.h> int main(void) { int x, i,j; for(x=1,i=1;i<=3;i++){ x*=i; } printf("3!は%dです\n",x); for(x=1, i=1;i<=4;i++){ x*=i; } printf("4!は%dです\n",x); for(x=1, i=1;i<=5;i++){ x*=i; } printf("5!は%dです\n",x); return 0; } |
なお,forループのところで,for( x=1, i=1; ...) としている.
これは,初期設定で x=1; i=1; を行いなさいということである.
for 文では,初期設定,条件,再設定を "; " で分けるようにし
ているため,初期設定や再設定で複数の文を実行したいときには,
"," を使って複数の文をならべるようにする.
もちろん,2重ループを使っても書けるが,煩雑になる.上記の プログラムでは同じ様なことを何度も繰り返しているが,この部 分に関数を使うと
#include <stdio.h> int fact(int n); int main(void) { printf("3!は%dです\n",fact(3)); printf("4!は%dです\n",fact(4)); printf("5!は%dです\n",fact(5)); return 0; } int fact(int n) { int i,r; for(i=1, r=1;i<=n;i++){ r*=i; } return r; } |
ずいぶん,読みやすくなる.Cでは一つあたりの関数のプログラム
量を小さくして読みやすくして,間違い(バグ)を起こしにくい
ようにすることが肝要である.
先のプログラムでは,階乗の計算をもし間違っていれば,プログ
ラム全体を変更しなければならない.
これに対し,後のプログラムでは,階乗の計算は関数で行ってい
るために,関数内のみを変更するだけでよい.
複雑な部分は関数に背負わせて,関数の作成のみに全力を傾けること
ができる.
なお,実数型を用いた,べき乗の値を計算するプログラム例を example5_2.cに示す.
関数の引数に関数を指定することもできる. example5_3.cに示す.
また,関数自身の中にその関数を指定することもできる.例えば, 先の整数値の階乗を計算する関数factは,
int fact(int n) { if( n == 0){ return 1; }else{ return n*fact(n-1); } } |
としても良い(どのように動作するかは,自分で考えてみよう). このように,関数内で自分自身を呼び出すことを 再帰呼び出しという. example5_5.c
引数を持たない場合や,戻り値を持たない場合には,"void" とい う言葉を使ってそれらを表す.この例を example5_4.cに示す.
関数では,引数で渡された変数は,もとの変数に影響を及ぼさない.
(影響を及ぼすようにすることも可能だが,この場合ポインターを使
う必要がある.)
#include <stdio.h> void swap(int x, int y); /* void swap(int, int); でも構いません*/ int main(void) { int x; int y; x=5; y=3; printf("関数を呼び出す x=%d, y=%d\n",x,y); swap(x,y); printf("関数の結果 x=%d, y=%d\n",x,y); return 0; } void swap( int x, int y) { int tmp; printf("関数に入ってきたとき x=%d, y=%d\n", x, y); tmp=y; y=x; x=tmp; printf("関数を出るとき x=%d, y=%d\n", x, y); /* 関数の戻り値がvoidならreturnはいらない*/ } |