このページでは、C言語における main
関数 の return
の役割について解説していきたいと思います。
意味も分からず、おまじないのように、とにかく main
関数に return 0;
を記述している方もおられるかもしれませんが、main
関数の return
での値の返却には重要な役割があります。C言語の学習中・プログラミングの練習中である方であれば、何も考えずに return 0;
を実行する or return
しないのでも良いのですが、実用性を考えると適切な値を return
で返却する方が本当は良いです。
この辺りの理由も含め、main
関数の return
の役割について説明していきたいと思います。
main
関数の return
の役割
基本的に、main
関数の return
の役割は他の関数における return
と同じで、関数を実行した相手に結果を伝えることが役割になります。
return
の役割
main
以外の関数の場合は、他の関数から呼び出しされることになるため、その呼び出し元の関数に対して関数の結果を伝えることになります。
それに対し、main
関数の場合はプログラム実行時に最初に呼び出しされる関数であるため、他の関数ではなくプログラム実行者に対してプログラムの結果を伝えることになります。言い方を変えれば、main
関数で return
した値がプログラムの結果として扱われます。
スポンサーリンク
「結果を伝える」ことの意味
では関数が「結果を伝える」こと自体の意味とは何になるでしょうか?
main
以外の関数の場合
これも main
以外の関数の場合と対比して考えると分かりやすいと思います。
main
以外の関数の場合は、呼び出し側の関数が関数の結果を利用できるようにするために関数が結果を伝えることになります。
例えば、ある関数が true
or false
を返却する場合、その関数の呼び出し側は返却値が true
の場合と false
の場合とで分岐を行い、それぞれに対して異なる処理を行うようなことが可能になります。
/* xが偶数であるかどうかを判断する */
bool is_even(int x) {
if (x % 2 == 0) {
return true;
} else {
return false;
}
}
void caller(int x) {
bool ret = is_even(x);
if (ret) {
printf("%dは偶数です\n", x);
} else {
printf("%dは奇数です\n", x);
}
}
また、ある関数が整数を返却する場合、その関数の呼び出し側で返却値を利用して単に表示を行うようなこともできますし、その返却値を利用した演算を行うようなことも可能になります。
/* 価格valueの半額を求める*/
int calcHalf(int value) {
return value / 2;
}
int caller(int value) {
return calcHalf(value) * 1.1;
}
main
関数の場合
では main
関数の場合はどうでしょう?
結局これも main
以外の関数の場合と同様です。ただし、呼び出し側の関数ではなく、プログラム実行者がプログラムの結果を利用できるようにするために main
関数が結果を伝えることになります。
では、このプログラム実行者とは誰になるのかというと、直接的には OS (Operating System) となるようです。つまり、プログラムの結果、すなわち main
関数から return
された値は OS が受け取ります。
そう聞くと一般ユーザーである我々からすると main
関数から return
される値は無関係のようにも思えますが、例えばシェルスクリプトやバッチファイル、さらには Python スクリプトや他のC言語プログラムからも、この main
関数から return
された値を受け取ることが可能です。そして、これらが main
関数から return
された値を受け取り、その値を利用することができます。
例えば、シェルスクリプトからプログラムを実行し、その後の処理にプログラムの結果を利用するようなこともできます。
プログラムの結果を利用する例
ちょっと抽象的な説明になっていますので、ここで具体例としてシェルスクリプトを用いた「プログラムの結果を利用する例」を紹介しておきたいと思います。
下記は、シェルスクリプトから sample.exe
というプログラムを実行し、プログラムが正常終了した場合のみ後続の処理を継続するような例となります。
#!/bin/sh
# sample.exeを実行
./sample.exe
# sample.exeの結果が0以外の場合はスクリプト終了
if [ "$?" -ne "0" ]; then
echo "sample error"
exit 1
fi
echo "next step..."
# 後続の処理(略)
いきなりシェルスクリプトを示されても意味不明と感じる方も多いと思いますので、ポイントのみ補足で説明しておきます。
まず、下記の行が sample.exe
の実行を行なっている部分になります(シェルスクリプトを実行しているフォルダ内に存在する sample.exe
を実行している)。
./sample.exe
sample.exe
はC言語のソースコードからコンパイルして生成されたプログラムであることを想定していますが、別に他の言語で記述されたプログラムでも良いですし、サイトからダウンロードしてきたようなプログラムでも良いです。要は、C言語のソースコードからコンパイルして生成されたプログラムも含め、プログラムは他のプログラムやスクリプト等から実行可能というところがポイントになります。
そして、sample.exe
の実行が終了すると、下記の処理が行われることになります。
# sample.exeの結果が0以外の場合はスクリプト終了
if [ "$?" -ne "0" ]; then
echo "sample error"
exit 1
fi
ここでポイントになるのが $?
です。この $?
は、直前に実行したプログラム(コマンド)の結果が格納されている変数になります。プログラムやコマンドが実行されるたびに自動的に $?
に結果が格納されます。上記においては、$?
に格納されている値が 0
でない場合はエラーであることを echo
で出力し、exit
でスクリプトを終了するようにしています。
そして、直前に実行されたプログラムのソースコードがC言語で記述されている場合、$?
には main
関数から return
された値が格納されることになります。
例えば、下記のようなソースコードからコンパイルを行なって sample.exe
を生成した場合、text.txt
が存在して開ける場合は main
関数が 0
を return
しますが、text.txt
が存在しない場合は 1
を return
することになります。
#include <stdio.h>
int main(void) {
FILE *fp;
fp = fopen("text.txt", "r");
if (fp == NULL) {
printf("%sが開けません\n", "text.txt");
return 1;
}
fclose(fp);
return 0;
}
したがって、上記のシェルスクリプトにおいては text.txt
が存在して開ける場合のみ $?
に 0
が格納され、後続の処理が継続して行われることになります(後続の処理の記述は省略していますが…)。
ですので、例えば後続の処理が text.txt
が存在しないと実行しても意味のないものであれば(例えば後続の処理が “text.txt
の文字数をカウントする” である場合など)、上記の if
文の中の exit
により直ちにスクリプトを終了することができて無駄な処理の実行を防ぐことができるようになります。
簡単な例ですが、シェルスクリプトからプログラムの実行結果を利用することでメリットがあることは伝わったのではないかと思います。
ここで、もう1つポイントを挙げておくと、それは上記のシェルスクリプトにおける exit 1
の部分になります。exit
はシェルスクリプトの処理を終了するコマンドなのですが、引数を指定することで特定の値を返却することができるようになっています。つまり、exit 1
により 1
が返却されることになります。そして、この返却値は上記のスクリプトの実行者が受け取ることができ、受け取った側でこの返却値を利用することが可能です。
このように、C言語に限らず、基本的にプログラムやスクリプトは実行者側に値を返却することができるようになっています。そして、その値を実行者側で利用してもらうことが可能となっています。
値の返却の仕方は言語等によって異なるので注意してください。例えば Python スクリプトでは return
ではなく sys.exit(返却値)
のように exit
関数を利用して値を返却するようになっています(返却しない場合は 0
が自動的に返却される)。
また、あなたがプログラムを実行したり、プログラムを実行するシェルスクリプトやバッチファイルを作成する場合は、プログラムの実行結果を受け取ることができるということも覚えておくと良いと思います。これにより、プログラムの結果に応じた処理の分岐などを実現するようなことも可能になります。
return
する値の重要性
ただし、上記の例のようにプログラムの結果を上手く利用することが可能なのは、プログラムの結果が適切に返却されるようになっている場合のみとなります。例えばエラーの有無などに関わらず、どんな場合でもプログラムの結果が 0
であるのであれば、プログラムの結果を利用する意味がありません。
つまり、プログラム実行者がプログラムの結果を上手く利用できるようにするためには、main
関数から return
する値を適切に設定するようにする必要があります。特にプログラムの結果は 0
の場合に「正常終了した」と考えるのが通例(特に Unix 系の OS では)なので、プログラムが正常に終了した場合は 0
を、プログラムがエラー終了した場合などは 0
以外を return
するようにしておいた方が良いです。
特に覚えておいていただきたいのは、プログラムは単体で動作するものだけでなく、他のプログラムと連携して動作するものも多いという点になります。例えば、あるプログラムを実行し、その実行結果がエラーの場合には後続の処理の実行を中止するようなことも多いです。
特にシェルスクリプトやバッチファイルで何かしらの手順を自動化する際は、このように複数のプログラムを連携して動作させるようなことが多いと思います。
このような、自身が開発したプログラムを利用して連携動作させるような場合は main
関数が return
する値が重要となりますので、main
関数から return
する値は適切に設定しておく方が良いです。
もちろん、勉強用に作成しているプログラムであったり、単体で動作することを前提としているプログラムのような場合は main
関数から return
する値はてきとうでも良いのですが、それでも main
関数の return
値は意味がないものではなく、この値を受け取る相手が存在することは覚えておくと良いと思います。
ちなみに、C言語の main
関数では return
の記述を省略することもできますが、この場合でも結局 main 関数の最後で return 0
が実行されるようになっています(C99以降)。
スポンサーリンク
まとめ
このページではC言語における main
関数の return
の役割について解説しました!
main
関数の return
の役割は「プログラム実行者にプログラムの結果を伝えること」になります。このプログラムの結果はシェルスクリプトやバッチファイル等で受け取ることができ、これらからプログラムの結果を利用することが可能になります。
単体で動作することを想定しているプログラムであれば main
関数の return
値はてきとうに 0 にしておいても良いですが、自身が開発したプログラムの結果を受け取る相手がいることは覚えておいた方が良いと思います。また、シェルスクリプトやバッチファイルを作成するような場合は、実行するプログラムの結果を取得可能であることも是非覚えておきましょう!
この結果を利用することで、シェルスクリプトやバッチファイル等で実現可能な処理の幅も広がると思います!
オススメの参考書(PR)
C言語学習中だけど分からないことが多くて挫折しそう...という方には、下記の「スッキリわかるC言語入門」がオススメです!
まず学習を進める上で、参考書は2冊持っておくことをオススメします。この理由は下記の2つです。
- 参考書によって、解説の仕方は異なる
- 読み手によって、理解しやすい解説の仕方は異なる
ある人の説明聞いても理解できなかったけど、他の人からちょっと違った観点での説明を聞いて「あー、そういうことね!」って簡単に理解できた経験をお持ちの方も多いのではないでしょうか?
それと同じで、1冊の参考書を読んで理解できない事も、他の参考書とは異なる内容の解説を読むことで理解できる可能性があります。
なので、参考書は2冊持っておいた方が学習時に挫折しにくいというのが私の考えです。
特に上記の「スッキリわかるC言語入門」は、他の参考書とは違った切り口での解説が豊富で、他の参考書で理解できなかった内容に対して違った観点での解説を読むことができ、オススメです。題名の通り「なぜそうなるのか?」がスッキリ理解できるような解説内容にもなっており、C言語入門書としてもかなり分かりやすい参考書だと思います。
もちろんネット等でも色んな観点からの解説を読むことが出来ますので、分からない点は別の人・別の参考書の解説を読んで解決していきましょう!もちろん私のサイトも参考にしていただけると嬉しいです!
入門用のオススメ参考書は下記ページでも紹介していますので、こちらも是非参考にしていただければと思います。
https://daeudaeu.com/c_reference_book/