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 値;」とする)ようにしてもかまいません。

課題10


C入門目次

前のページ  後のページ