7.関数
7.1.関数の作成
C言語では自分で関数を作成することもできます。自分で関数を作成する場合も、以下の関数の形式に従います。
返り値の型 関数名(引数の型 引数名,...){ +----------------------------------+ | | | 関数内部の処理 | | | +----------------------------------+ }
以下のサンプルは1から10まで、それぞれの3乗値を計算して出力するプログラムです。
#include <stdio.h> int power_3(int value){ return(value*value*value); } int main(int argc,char *argv[]){ int i,value; for(i=1;i<=10;i++){ value=power_3(i); printf("%d^3=%d\n",i,value); } return(0); }
3〜5行目で引数で与えられた数の3乗を返す関数を定義し、10行目でその関数を呼び出しています。自作の関数を呼び出す場合は、それまでに定義されているか、プロトタイプ宣言されている必要があります。プロトタイプ宣言とは、先に関数の引数の型および返り値の型だけを明らかにしておき、中身の実装を後回しにすることです。例えば上のサンプルでは以下のようになります。(3行目以下のみ)
int power_3(int); int main(int argc,char *argv[]){ int i; for(i=1;i<=10;i++){ printf("%d^3=%d\n",i,power_3(i)); } return(0); } int power_3(int value){ return(value*value*value); }
(実習課題)
以下のプログラムを作成しなさい。
- キーボードから数字を入力し、それを受け取る。
- 1からその値までの階乗値(n!)の値を表示する。
- 入力値は1以上10以下の整数に限定する。それ以外の値の場合はエラー表示。
- 「キーボードから入力を受け取って数字を返す部分」と「階乗値を計算して返す部分」の2つはmain関数とは別の関数にすること。
7.2.関数とポインタ
関数は引数として、ポインタを取ることもできます。しかしポインタを使う場合は、挙動が少し異なるので注意が必要です。
#include <stdio.h> void inc(int a){ a++; printf("inc:a=%d\n",a); } int main(void){ int a=10; inc(a); printf("main:a=%d\n",a); return(0); }
例えば上のサンプルプログラムを実行すると、「inc:a=11」「main:a=10」と表示されます。inc関数の中でのaの増加が、呼び出し元のmain関数のaには反映されていません。これは関数に引数として変数の値が渡される際、関数内部にまったく別のデータ領域をもった変数が新しく作成され、それに引数のデータがコピーされるためです。したがって関数内での処理の影響を、元の変数は受けません。上のサンプルでは、main関数の変数aはinc内での処理に影響されません。
#include <stdio.h> void inc(int* a){ (*a)++; printf("inc:a=%d\n",*a); } int main(void){ int a=10; inc(&a); printf("main:a=%d\n",a); return(0); }
しかしポインタを使用した上のサンプルを実行すると、「inc:a=11」「main:a=11」と同じ値が表示されます。これは関数に変数を渡す際に、変数の値ではなく、データ領域のアドレスがコピーされるためです。incの中でポインタaは、main関数の変数aのデータ領域アドレスを指し示しているので、実質どちらも同じデータ領域を保持していることになります。したがってポインタが指し示す値を増加させると、main関数内の変数aの値も増加することになります。この方法は「gets」関数でも使用されています。getsは引数としてchar型のポインタをとり、このポインタが指し示すデータ領域に標準入力から読み込んだ文字列を格納しています。
ポインタによるデータの引き渡しは、関数から2つ以上の処理の結果を取り出したい時に使用できるので、非常に便利です。
(実習課題)
以下のプログラムを作成しなさい。
- コマンドラインからプログラムに渡されるオプションは2つ。
- 1つ目のオプションで指定されたファイルの中身を、2つ目のオプションで指定されたファイルにコピーする。
- コピーの際、行の順番が逆になるようにする。つまり1つ目のファイルの先頭行が2つ目のファイルの最終行に、1つ目の最終行が2つ目の先頭行になるようにする。
- 1つ目のファイルは、最大10行で1行の文字数は最大256とする。
- ファイルを読み込む部分と、ファイルに書き込む部分を別関数にすること。ただしグローバル変数を用いてはならない。
- (ヒント)ファイルを読み込む関数の引数は「読み込むファイル名」および「データを格納する配列」。返り値は「読み込んだ行数」。
- (ヒント)ファイルに書き込む関数の引数は「書き込むファイル名」「データを格納した配列」「読み込まれた行数」の3つ。
7.3.再帰呼び出し
「再帰呼び出し」とは、自分自身が自分自身の関数を呼び出すことです。main以外の自作の関数は再帰呼び出しを実現することができます。以下は1から100までの足し算を再帰呼び出しを利用して実行するサンプルプログラムです。
#include <stdio.h> int sum(int value){ if(value==0){ return(0); }else{ return(value+sum(value-1)); } } int main(void){ printf("sum=%d\n",add(100)); return(0); }
関数「sum」は与えられた引数までの合計を返す関数です。valueまでの合計とは、value-1までの合計にvalueを加えたものですので、その部分に再帰呼び出しを使用しています(7行目)。再帰呼び出しは鏡の前に鏡を置いた状態と同じで、単純に使用すると「鏡の中に鏡が写り、その鏡の中に鏡が写り...」というような無限ループに陥ってしまいます。したがって必ず再帰呼び出しを行わない条件を、関数に埋め込まなければなりません。サンプルでは4行目にその条件が記されています。
(実習課題)
以下のプログラムを作成しなさい。
- キーボードから入力された数字を受け取る。
- 入力される数字は1以上100以下で、それ以外の場合はエラー表示する。
- 入力された数字の階乗値(n!)を計算して出力する。
- 階乗値の計算には再帰呼び出しを利用すること。
- またキーボードから数字を読み込む部分は別関数にすること。