Cによるプログラミング入門10
値を戻す関数
これまで、「値を戻す」だの「値を戻さない」など、なんだかいやな感じがしていたかもしれません。
今回こそ、すべてを説明し、すっきりしてもらいます。
Cで関数とは、何かしてくれるもののことでした。たとえば、printfがそうです。...、もうあきましたね、この話は。しかし、まだあるのです。たとえば、乱数を発生させる関数randというものがありましたね。この関数は、それが書かれている場所に、「乱数が置いてくれる」ようなものでした。
つまり、
x = rand();
とあれば、xに発生した乱数が格納され、
printf("%d", rand());
とあれば、rand()の場所に乱数が置かれ、それが出力されるのでした。
実は、このような場合、「randは乱数を戻す」とか「乱数を返す」というのです。ちょっとびっくりする用語法ですが、「戻す」も「返す」も、英語のreturnの訳語で(したがって同じ意味です)、日常生活の「使ったものはちゃんと戻しておきなさい」とか「俺の金を返せよ」などとは何の関係もありません。とにかく、上のようになることを、「randは乱数を戻す」などと言うのです。
また、「乱数」は、一般的に言うと「値」ですから、「randは値を戻す関数」などと言うことができます。
まだピンとこないでしょう。ここで、「値を戻す関数」を自作してみると、実感がわくのではと思います。たとえば、次の関数threeは、「つねに3を戻す関数」です。(断言しておきますが、もちろん、実用性はありません。説明のための関数です。)
int three(void){
return 3;
}
ここで、threeが関数の名前です。そして、関数のあたまには、前回のようなvoidではなく、intが付いていることに注意してください。これは、「この関数はint(つまり整数)を戻すよ」という印なのです。逆に、今まで書いていたvoidは、「この関数は値を戻さないよ」という印だったのです。
そして、実際に戻すものは、「return」とその後の「;」の間に書くことになっています。つまり、threeは、「3」という整数を戻すのです。
まだすっきりしないと思いますが、例を見てから考えてください。たとえば、つぎのプログラムです。
/* three1.c */
#include <stdio.h>
int three(void){
return 3;
}
int main(void){
int x;
x = three();
printf("xの中身は%dです。\n", x);
return 0;
}
Fig.1 three1.exe
「x = three();」では、関数threeが実行され(「呼び出され」というのでした)、その結果、3が戻され(つまり「その場所に3が置かれ」て)、xに3が代入されることになるのです。
また、randの場合と同様に、threeを、
printf("%d", three());
のように使うこともできます。これを実行すると「3」と出力するはずです。
おもしろいことに、「値を戻す関数」が、値を戻すこと以外をしてもかまいません。たとえば、threeを
int three(void){
printf("はろー。みんな元気か?\n");
return 3;
}
とすると、関数threeが3を戻す前に、「はろー。みんな元気か?」を出力することになります。たとえば、次のようなプログラム実行するとどうなるでしょう。
/* three2.c */
#include <stdio.h>
int three(void){
printf("はろー。みんな元気か?\n");
return 3;
}
int main(void){
int x;
x = three();
printf("xの中身は%dです。\n", x);
return 0;
}
実行すると、次のようになるのです。
Fig.2 three2.exe
基本的には、前のプログラム(three1.c)と同じです。「x
=
three();」というコードでは、関数threeが実行され、その結果、3が戻され、xに3が代入されるのでした。しかし、「関数threeが実行される」ときに、「はろー。みんな元気か?」が出力されるので、実行結果が上のようになるのです。
threeは次のようにも利用できます。
/* three3.c */
#include <stdio.h>
int three(void){
printf("はろー。みんな元気か?\n");
return 3;
}
int main(void){
printf("%d\n", three());
return 0;
}
Fig.3 three3.exe
どうしてこのような結果になるかわかるでしょうか。「printf("%d\n", three());」では、まず、three()が実行され、戻された値の3がprintfで出力されるからです。そういうものなのです。なんだか、すごいですね。
もう少し練習してみましょう。たとえば、threeという関数を次のように書き換えたとすると、それはどういうものだかわかりますか?(もちろん、名前は何でもよいのですが。)
int three(int x){
printf("引数として受け取った値は%dでした。\n",
x);
printf("その3倍を戻します。\n");
return x * 3;
}
これは、引数を受け取り、その値などを表示し、引数の3倍を戻す関数です。この関数を使う次のようなプログラムを実行するとどうなるか、ちょっと考えてみてください。
/* three4.c */
#include <stdio.h>
int three(int x){
printf("引数として受け取った値は%dでした。\n",
x);
printf("その3倍を戻します。\n");
return x * 3;
}
int main(void){
int x, y;
printf("好きな整数を入力してください。その3倍を表示します。\n");
scanf("%d", &x);
y = three(x);
printf("yの中身は%dです。\n", y);
}
Fig.4 three4.exe
実行画面で、
引数として受け取った値は5でした。
その3倍を戻します。
と出力されているところが、threeが実行されているところです。
ところで、上のプログラムでは、mainの中で変数xを定義し、そこにユーザの入力値を格納し、それをthreeの引数にしているのでしたね。threeの仮引数(パラメータ)の名前もxです。これは関係あるのでしょうか?
答は「ありません」です。仮引数は、関数の定義の中だけで有効なもので、その外(mainの中は関数threeの外ですね)にあるものとは無関係なのです。したがって、mainの中のxをすべて別の文字に置き換えても問題ありません。
また、上のthreeに対して、次のようなプログラムを書くこともできます。
/* three5.c */
#include <stdio.h>
int three(int x){
printf("引数として受け取った値は%dでした。\n",
x);
printf("その3倍を戻します。\n");
return x * 3;
}
int main(void){
int x;
printf("好きな整数を入力してください。その3倍を表示します。\n");
scanf("%d", &x);
x = three(x);
printf("xの中身は%dです。\n", x);
}
この「x = three(x);」は、初心者にはややこしいですが、C(など)ではよく現れるパターンです。これで、「xを引数に右辺のthree(x)を実行し、その結果戻される値をxに格納する」ということになるのです。これも、そういうものなのです。
Fig.5 three5.exeの実行
「戻す」の意味をもう一度考えてみます。たとえば、mainの中に「three();」と書いてあれば、それは、「そこでthreeの定義に書いてあるコードを実行する」という意味です。ところで、threeの定義は、そこ(three()と書いてある場所)ではなく、別のところに書いてあるはずです。そこで、プログラムの実行場所が、mainの中から「threeの定義の場所」に移るわけです。そして、threeの実行が終わると、もとの「three();」が書いてある場所に、実行場所が戻るわけです。だから「戻る」というのでしょう。
なお、関数が戻す値を戻り値とか返り値といいます。
次に、別の関数を考えてみましょう。
int which(void){
int a;
printf("あなたの年齢を入力してください。\n");
scanf("%d", &a);
if(a < 20){
return 0;
}
else{
return 1;
}
}
これは、年齢を入力させ、それが20未満なら0を戻し、そうでなければ1を戻す関数です。
この関数使って、次のようなプログラムを書くこともできます。
/* seinen.c */
#include <stdio.h>
int which(void){
int a;
printf("あなたの年齢を入力してください。\n");
scanf("%d", &a);
if(a < 20){
return 0;
}
else{
return 1;
}
}
int main(void)
{
int x;
char* hyouji[2] = {"未成年ですな。", "もうお酒が飲めますね。"};
x = which();
printf(hyouji[x]);
return 0;
}
このプログラムもあまり意味のあるものではありません。ただ、「値を戻す関数」のしくみを見るためだけのものです。これを実行すると、次のようになります。
Fig.6 which.exe
(余計なお世話です。)
たとえば、「ユーザとじゃんけんをして、その結果を戻す関数」なら、もう少し役に立ちそうです。これは課題ということにしましょう。
実は、returnには、関数を終了させるという意味があります。「return
値;」とあれば、関数はそこで終了します。そういうものなのです。
それでは、値を戻さない関数(あたまにvoidの付く関数)に関して、途中で実行を終了させるにはどうすればよいでしょうか。それは、単に「return;」と書けばよいのです。たとえば、次の関数を見てください。
void which(void){
int a;
printf("あなたの年齢を入力してください。\n");
scanf("%d", &a);
if(a < 20){
return;
}
printf("もうお酒が飲めますね。\n");
}
これは、ユーザが20未満の整数を入力した場合には、何もせずに終了する関数です。逆に、20以上の整数に対しては「もうお酒が飲めますね。」と出力しますね。
なお、一般に、「return 値;」や「return;」をreturn文などとも言います。
実は、mainは、「プログラムの中心」と書いてきましたが、mainも関数なのです。プログラムの実行時に、最初に呼び出される特別なものですが、やはり、関数なのです。
そして、あたまにintが付いているのは、mainが、intを戻す関数ということなのです。mainはそのように決められているわけです。そして、一般に、「プログラムが正常終了したときには、mainは0を戻すようにする」という慣習があります。そのため、mainは
int main(void)
{
...
return 0;
}
などと書くのです。もちろん、「...」の部分で途中終了させたい場合、何か値を戻して終了する(「return 値;」とする)ようにしてもかまいません。