どうも〜 シュモクザメです。
今回はCに限らず全ての言語で大事な戻り値(返り値)について解説してみたいと思います。
初めてプログラミングをしたときに生じた疑問
僕は大学に入ってから、すぐに情報の講義でプログラミングに触れたのですが、その時の初めての課題で思ったことです。

printfとreturnって何が違うの?
今思えば、何を言っとるんやこいつは、、って感じの疑問ですが、マジで最初は思ってました。
まあそのときは課題の内容が「計算してprintfで出力せよ」的なものだったので僕の中で
コードの処理 = printfで出力
だと思っていたから説はあると思います。
もし今プログラミングを初めてこんな感じの疑問にぶつかっていたり、関数のreturnがよくわからないって人の助けになれば幸いです。
今回考えてみる課題
解説のために、一つ課題を考えてみましょう。
1×2, 2×2, 2×3を計算してその結果を出力せよ
まあ簡単なものですがしっかりみていきます。
色々な解き方
main関数のみで解く方法
まずは一番脳死のmainのみで解く方法をやってみましょう。以下がコードです。
int main(int argc, const char * argv[]) {
int a,b,c;
a = 1 * 2;
b = 2 * 2;
c = 3 * 2;
printf("%d %d %d\n",a,b,c);
return 0;
}
まあそのままですよね。
そもそもの課題が簡単なので普通にこれでもおkなんですが、次は関数を用いて考えてみます。
関数を作って解く方法1
お気づきだと思いますが、この課題は1,2,3をそれぞれ2倍したものを出力してますよね。
なので2倍して出力する処理を行う関数を定義してみます。
void x2_A(int k){
int d = k * 2;
printf("%d ",d);
}
int main(int argc, const char * argv[]) {
x2_A(1);
x2_A(2);
x2_A(3);
return 0;
}
関数x2_Aでは引数で受け取ったkを2倍してdに代入して、それをprintfで出力しています。
ここで大事なのはこの関数の型がvoidで、関数内でprintfで出力まで行っている点です。
言い換えると、main関数内ではprintfの記述がないってことです
関数を作って解く方法2
次は以下のコードを見てみてください
int x2_B(int k){
int d = k * 2;
return d;
}
int main(int argc, const char * argv[]) {
printf("%d",x2_B(1));
printf("%d",x2_B(2));
printf("%d",x2_B(3));
return 0;
}
今回の関数x2_Bでは関数の型がintで、関数内にprintfの出力がなく、2倍した値をreturnしている点です。
言い換えると、main関数内でprintf出力をしているってことです。
関数x2_Aとx2_Bの違い
上のコードで2つの違いがわかりましたか?
もちろんどちらのコードでも計算は合っていますし、課題自体はクリアしています。
ただ処理の流れは全然違うものとなっています。
x2_A(void型)の場合の処理
この方法のイメージは以下です。

mainから計算の処理を関数x2_Aに依頼したらあとは出力まで丸投げ、って感じです。
つまり計算を依頼してからその結果はmain関数君は知らない、、、っていうイメージですかね。
(ややこしいんですが、printfで出力はしているので僕たちは結果をその出力で知ることができます)
というのもこの関数x2_Aがvoid型、つまり値を返さない型だからです。
逆に言えば計算の結果をmain関数君に知らせたくても知らせる手段が無い、、って感じです(かわいそう)
x2_B(int型)の場合の処理
この方法のイメージは以下の図です。

main関数から計算の処理を関数x2_Bに依頼、その結果をmain関数に送って(返して)main関数内のprintfで出力って感じです。
つまりmain関数君が依頼したのはあくまで計算の処理だけで、その結果はmain関数君にreturnで返してもらって、結果発表(printf)はmain関数君が行っているって感じですかね。
これは関数x2_Aがint型、つまりint型の値を返すことができる関数だからです。
ちなみに値を返すっていうのは、その関数自体が値を持つってことです。
上の例のmain関数のprintfをもう一度見てみると、
printf("%d %d %d\n",x2_B(1),x2_B(2),x2_B(3));
こんな感じで普段変数や値を入れている場所に関数が入っていますね。
これはこの関数自体が引数を受け取った時の結果の値が格納された変数と見て良いからです。
関数にreturnを入れるか入れないかの基準
この課題でややこしい部分が、
求めていることが「計算結果の出力」である点です。
出力関数、つまりprintfは外部の関数内に記述したときもしっかりとコンソールに出力されるので、今回考えた課題ではx2_Aもx2_Bも正解になっちゃうんです。
これがprintfとreturnの違いや、returnをした方が良いのかどうなのかをわかりにくくする要因じゃ無いかな〜って思います。

まあ最初の方の課題って大体出力をもとめるからね、、
ここでreturnした方が良い場合と、return無し(void型)で良い場合を例を挙げて考えてみましょう
returnした方が良い場合
これは一言で言うと、
その関数内で計算した結果をmain関数や他の関数内でさらに処理を行う場合はreturnした方が良いです。
今回の課題では2倍した値を計算して終わりだったのですが、例えば
1×2 と 2×2 と 3×3 の結果の和を求めて出力せよ
みたいな課題だったら、2倍した値をさらに足す処理が必要です。
int x2_B(int k){
int d = k * 2;
return d;
}
int main(int argc, const char * argv[]) {
int s = x2_B(1) + x2_B(2) + x2_B(3);
printf("%d\n",s);
return 0;
}
⬆︎こんな感じね
そのときにvoid型の関数で2倍する処理だと2倍した値をmain関数に持って帰れないので、関数の意味がないですよね。(mainに限らず他の関数で使う時も同様)
return しなくても良い場合
これも一言で言うと、
その関数内の結果を他の関数で使う機会がなく、そこで完結させて良い場合です。
まあ今回の課題のパターンとかですね。
他にも例を挙げとくと、こんな感じです。
void judge(int a){
if(a>100){
printf("over!!\n");
}
}
int main(int argc, const char * argv[]) {
judge(200);
judge(33);
judge(555);
return 0;
}
これは引数の値が100より大きいか判定する関数judgeです。
もし大きければover!と出力させたいのなら、わざわざreturnでmainに返す必要はないですよね。
このときは上のコードのようにvoidでおkですね。
まとめ
以上でreturnについての解説はおしまいです。
まあとにかく何回もコードを書いていくうちに理解していく部分でもあると思います。
誰かの役に立てば幸いです!
ではまた!
コメント