#include <stdio.h> int max(int a, int b) { int m; if (a > b) { m = a; } else { m = b; } return m; } main( ) { int n1, n2, dai; printf(“整数を二つ入力してください。¥n”); printf(“整数 1:”); scanf(“%d”, &n1); printf(“整数 2:”); scanf(“%d”, &n2); dai = max(n1, n2); printf(“大きい方の数字は%d です。¥n”, dai); }
6関数
6-1 関数とは
少し長いプログラムを作るようになると、同じ処理を何度も行う場面が出てくる。そのたびに処
理を書いていたのでは明らかに無駄であるし、プログラム全体の見通しも悪くなる。そこで登場す
るのが「関数」である。関数を使うことを、
「関数を呼び出す」ともいう。
どのように使うのか、実際に見てみよう。次のプログラムは、2 つの数字のうち、大きい方を求
めるものである。
「#include」の行と「main」の行にはさまれた
のが関数部分である。
・最初の「int」は、「int 型の値を返しますよ」と
いう宣言。値を返さない場合もあるが、それは後
ほど。
・次の「max」が関数の名前。
・関数名のあとの( )の中は、
「呼び出し元からこう
いう変数を受け取ります」という意味。これを「仮
引数(かりひきすう)」という。仮引数は、ここで型
宣言する必要がある。仮引数のない関数もあるが、
それも後ほど。
次の{ }内(ブロック内)が関数が行う処理の内容
である。ここは、
main とほぼ同じであるが、いく
つか注意点がある。
①仮引数は型宣言済みと考えて良いので、ブロッ
ク内で改めて宣言する必要はない。
②関数で新たに決める変数の名前は、呼び出し元
の名前とかぶっても構わない。別物として扱われる。
③値を返す場合は、return に続けて返す値を明示する。ここでは変数が返されているが、定数で
も構わない。
この例を詳しく見てみよう。最初の行は「この関数は”max”という名前で、int 型の二つの値を
受け取り、それを使って
int 型の値を返します」という意味になる。
int
max
(int a, int b)
返す値の型
関数の名前
呼び出し元から受け取った値
次に、a,b はすでに宣言済みなので、新たに必要な m だけ定義しておく。m には、a と b の内の
大きい方(正確には a=b のときは b)が代入される。
/* 引数のない関数 ( ② ) の例 */ #include <stdio.h> #include <stdlib.h> #include <time.h> /* 1 から 100 までの乱数を発生して返す 関数 */ int int_rand( ) { return rand() % 100 + 1; } main( ) { int num; srand(time(NULL)); num = int_rand( ); printf ( “乱数:%d¥n”, num); }
では、
main の方では関数をどのように使っているのか。利用しているのは、
「dai = max(n1, n2);」
の部分。関数呼び出し部と、関数本体の関係は次のようなイメージ。
「max(n1, n2)」で、関数 max に n1 と n2 を渡
して処理してもらう。この
n1 と n2 を「実引数(じ
つひきすう)」と呼ぶ。max 関数はそれを受け取り、
m を返す。言い換えれば m= max(n1, n2)とも言
え、すなわち
dai =m(関数 max 内での)。
ここで見て欲しいのが、main で関数に渡した
変数の名前と、関数で受け取った関数の名前が違
うということ。呼び出し元から関数に送るのは値
そのものであり(現段階では)、変数名は便宜上の
ものと考えていい。もちろん同じでもいいが、同
姓同名の別人だと思って欲しい。
引数と仮引数の対応についての注意。
引数と仮引数は、個数、並び順、型がすべて同じ
でなくてはならない。
次に、戻り値と引数(仮引数)に付いてみていこう。上の例では、引数があって戻り値もあったが、
処理内容によってはどちらとも無くてもよい。したがって、次の
4 通りが考えられる。ちなみに、
戻り値がないときの型宣言は「void」とし、引数がないときは( )内をからにしておく。
① 戻り値、引数ともにある …… type name(仮引数) { }
② 戻り値のみある …… type name ( ) { }
③ 引数はあるが、戻り値はない…… void name(仮引数) { }
④ 戻り値、引数ともに無い …… void name ( ) { }
①はすでに登場したので、②~④の実例を見てみよう。
左のプログラムにある関数は、1~100 の整数の(疑
似)乱数を発生させるものである。
乱数を発生させるコマンドが「rand( )」の部分で、C
言語では一般的に
0~32767 までの整数を返す。rand( )
を使用するためには、
「stdlib.h」が必要となる。
rand ( ) %100 で 100 で割った余りが求まるので、0
~99 が得られる。それに 1 を足せば、1~100 が得られ
ることになるので、それを返す。
main 内の「srand(time(NULL));」は、乱数の種を
初期化するためのおまじないみたいなものだと思って
欲しい。ここで
time( )を使っているので、
「time.h」が
必要となる。乱数を使うときの定番として覚えておく
といい。
/* 引数があって戻り値のない関数 ( ③ )の例 */
#include <stdio.h>
/* 指定した文字を、指定回数繰り返し表示する 関数 */
void show(char c, int n ) { int i; for (i = 0; i < n; i++) { printf(“%c”, c); } printf(“¥n”); } main( ) { int num; char moji;
printf(“出力する文字:”); scanf(“%c”, &moji); printf(“出力する回数:”); scanf(“%d”, &num); show(moji, num); } /* 引数も戻り値もない関数( ④ )の例 */ #include <stdio.h> /* 時間稼ぎの関数(一定時間何もしない) */ void timer( ) { int i; for (i = 0; i < 10000; i++) { printf(“¥r”); } printf(“¥n”); } main( ) { printf(“明日は¥n”); timer( ); printf(“晴れです。¥n”); } #include <stdio.h> void swap(int a, int b ) { int t; t = a; a = b; b = t; } main( ) { int a = 5, b = 10; swap(a, b); printf(“a=%d b=%d¥n”, a, b); }
このプログラムは
main で文字と数値を代入
し、それを関数に送って文字を指定回数表示させ
ている。関数は表示を行うだけなので、戻り値は
ない。
最後は、引数も戻り値もない例。このプログラ
ムを実行すると「明日は」と表示した後、ちょっ
と間をおいて「晴れです。
」と表示する。
関数では
1 万回「¥r」を表示しているが、見
た目は何もしてないように見える。時間稼ぎなの
で「printf」は無くてもいいのだが、本当に何も
しないと処理時間が短すぎるのでこうしてみた。
6-2 実引数と仮引数の関係
先に、関数が受け取る仮引数は、値そのものと書いた。言
い換えれば、コピーを渡すと考えてもいい。次のプログラム
は、関数で変数の値を交換しようとして作った。実行すると
どうなるか。値は変化していないはずである。というのも、
関数にはコピーを送っただけで、本体の変数は何もいじって
いないからである。これを思った通りの動きをさせるために
は、ポインタの知識が必要となる。
#include <stdio.h> int max(int ten[ ]) { int max, i;
max = ten[0];
for (i = 1; i < 5; i++) {
if (ten[i] > max) max = tem[i]; } return max; } main( ) { int point[5] = {10 , 90 , 75 , 45 , 80}; printf(“最高点は%d です¥n”, max(point); } #include <stdio.h> void init(int ten[ ]) { int i; for (i = 1; i < 5; i++) { ten[i] = 0; } } main( ) { int point[5] = {10 , 90 , 75 , 45 , 80}; int i; init(point); for (i = 0; i < 5; i++) { printf(“point[%d] = %d”, i, point[i]); }
6-3 配列を引数にする
配列を引数とするときは、次のような書き方をする。
仮引数で、配列の宣言をする。1 次元配列の場
合は、要素数を省略できる。多次元配列でも、最
初の要素数だけは省略できるので、int num[ ][3]
のような書き方ができる。実引数は
1 次元でも多
次元でも、配列名だけを書けばよい。
上の例では配列の値を利用するだけで、配列そ
のものへの変更を加えていない。では、左のよう
なプログラムを実行したらどうなるか。下の実行
結果が示すとおり、すべて
0 になってしまう。
というのも、普通の変数の場合は値のコピーを渡していたが、配列の場合は、配列そのものを渡
しているようなものとなる(厳密には違うが、これを正確に理解するためにはポインタの知識が必
要)。したがって、引数として配列を使う場合には、十分注意が必要となる。
6-4 関数プロトタイプ
多くの入門書では、関数を使う場合
main の前に関数を置いていると思う。関数の置き方はもう
一つ、
main の後に置く方法もある。この場合、関数プロトタイプといって、使用する関数の名前、
戻り値、仮引数を
main の前に宣言する必要がある。書式は次の通り。
int
max
(int a, int b)
;
返す値の型
関数の名前
呼び出し元から受け取る値
関数の
1 行目と同じ書き方だが、最後に「;」を付けるのを忘れないこと。また、仮引数の名前
は、実際の関数で使う名前と違っても構わない。関数プロトタイプで分かり易い名前を使って、関
数の方では短い名前を使うというのも一つの手である。
実行結果 point[0] = 0 point[1] = 0 point[2] = 0 point[3] = 0 point[4] = 0さて、関数を前に置くのと後に置くのでは、どちらがよいのだろうか。小さなプログラムの場合
は、前に置いた方が関数プロトタイプを記述しない分、楽ではあるが、正当とされているのは後に
置く方法である。関数を前に置く方法だと、
・互いに呼び合う関数の記述ができない。
・呼ばれる関数を先に記述しなければならないため、関数のリスト上の出現順序が不自然になる。
・機能ごとに並べたり、abc 順に並べたりできない。
・他人のプログラムと異なったスタイルになる。
といった不利が生じる。また関数プロトタイプを記述することにより
・プログラムの先頭部分を見るだけで、そのプログラムの中で使われている関数の一覧情報が分る。
というメリットがある。
次のプログラムは、配列要素内の数だけ指定した文字を表示するプログラムだが、 いくつか間違いがある。その間違いを修正せよ。
#include <stdio.h> int show(char ch, int a) { int i, j; for (i = 0; i < 5; i++) { printf(“a[%d]:”, i); for (j = 0; j < a[i]; j++) { printf(“%d”, ch); } printf(“¥n”); } } main() { int i; int num[5] = {5, 4, 0, 3, 7}; char moji = „*‟; Show(num, moji); } 問題 6-2 次のプログラムの□内を埋めよ。 #include <stdio.h> /* 平均を求めて返す関数 */
① heikin( ② kei, ③ num) { return kei / num;
} main() { int i;
int point[5] = {25, 50, 80, 10,100};
double total = 0.0, ave; /* total:合計 ave:平均 */ for (i = 0; i < 10; i++) { total += ④ ; } ave = ⑤ ; /* ave に関数を使って平均を代入する */ printf(“合計:%3.0f 平均:%5.1f¥n”, ⑥ , ⑦ ); } 問題 6-1 実行結果 a[0] :***** a[1] :**** a[2] : a[3] :*** a[4] :*******
<解答>
#include <stdio.h>
/* 平均を求めて返す関数 */
① double heikin( ② double kei, ③ int num) { return kei / num;
} main() { int i;
int point[5] = {25, 50, 80, 10,100};
double total = 0.0, ave; /* total:合計 ave:平均 */ for (i = 0; i < 10; i++) {
total += ④ point[i] ; }
ave = ⑤ heikin(total , 5); /* ave に関数を使って平均を代入する */ printf(“合計:%3.0f 平均:%5.1f¥n”, ⑥ total , ⑦ ave ); }
問題 6-1
#include <stdio.h>
int show(char ch, int a[ ]) { /* 配列を受け取るので、仮引数も配列 */ int i, j; for (i = 0; i < 5; i++) { printf(“a[%d]:”, i); for (j = 0; j < a[i]; j++) { printf(“%c”, ch); /* 文字を表示するので、%c */ } printf(“¥n”); } } main() { int i; int num[5] = {5, 4, 0, 3, 7}; char moji = „*‟;
show(moji, num); /* 名前は、大文字小文字を区別。引数の並びが違う */
}