質問: C++の勉強を最近始めました。 ふと思ったのですが書籍等では、main関数の宣言時に戻り値の指定を基本的にintと書いてあります。 こんな感じです ``` #include using namespace std; int main() { ... return 0; } ``` これを書いていて思ったのですが、戻り値にintを指定して、 return 0;を返すことに意味はあるのでしょうか? 初学者の私はJavaみたいにvoidで戻り値を指定しておけば良くない?? と思ってしまったのですが、 このことについて何かしらのアドバイスをいただけたらと思います。 初歩的な質問で申し訳ございませんが、よろしくお願いいたします。
回答: `main`関数からの戻り値はプロセスの終了ステータスになります。 プログラムの中にはプログラムの内部でプログラムを実行する(Cであれば`system`関数等)場合があります。呼び出す側のプロセスを親プロセス、呼び出される側のプロセスを子プロセスと言います。あるプログラムAが実行され、そのプロセスが動作中に、別のプログラムBを内部で実行したとしましょう。その時、プロセスは親子関係にあり、親プロセスではプログラムAが、子プロセスではプログラムBが動作することになります。プログラムBのコードを全て実行し、子プロセスが終了したとします。そのとき、子プロセスは親プロセスに終了ステータスを渡します。プログラムAではその終了ステータスによって動作を分岐させることができます。 この終了ステータスは整数値(とり得る範囲は環境によって異なる)で、ほとんどの環境では0が成功終了(正常終了)です。Cでは、この終了ステータスを簡単に返せるようにと、`main`関数の戻り値によって、どんな終了ステータスで終了したかを選べるようにしています。Cの中でも、0が成功終了を意味しており、`return 0;`となっている場合は、そのプロセスは成功終了を意味する終了ステータスを渡すようになっています。 なお、`exit`関数で即座に完了することもでき、`exit`関数への引数も同じように解釈されます。(逆に言うと、`return 0;`は`exit(0);`と同じです。) C++はCの拡張であるため、Cのこの仕様を引き継いでおり、同様の動作となります。 FAQ 1. 成功終了を表す終了ステータスが0以外の環境(存在するかは不明)で、`return 0;`とした場合は、異常終了になるのですか? いいえ、0または`EXIT_SUCCESS`(成功終了を表すマクロ、0とは限りない)を`main`でreturn(またはexit関数に渡す)した場合、その処理系における成功終了を表す終了ステータスになります。`main`の`return 0;`はどのような状況においても、成功終了を意味します。 2. 0以外は異常終了なのですか? Cの仕様で定義されているのは`EXIT_FAILURE`の場合に失敗終了を意味するということだけです。それ以外は処理依存になります。ただし、ほとんどの処理系では、0-255の範囲ではそのまま終了ステータスになり、0以外は異常とみなされます。 3. `return 0;`を省略しましたが、正常終了しました。バグですか? 戻り値が`void`ではない関数の中で、唯一`main`関数のみ`return`を省略出来ます。省略された場合は、一番最後に`return 0;`が存在するとみなされます。 4. どういうときに使うのですか? 特にシェルで多用します。`prog_1 && prog_2`と書くと、prog_1が成功終了したばあいのみ、prog_2が実行されるということができます。その他、Bash等では`$?`で直前に実行したコマンドの終了ステータスを取得することができます。 Windowsのバッチでは`%ERRORLEVEL%`や`IF ERRORLEVEL 1 goto error`といった使い方ができます。 5. パイプとは関係ありますか? パイプは標準入出力を使った機能で、より多くの情報をプロセス間でやり取りできます。プロセス間の情報のやり取りと言えば同じですが、終了ステータスは子から親への整数値のみと言うかなり限られた物で、使い道も異なります。 6. なぜ、Javaの`main`では戻り値がないんですか? Javaを含めた現代的言語のほとんどは`exit`関数に相当する物で終了ステータス指定するようにしています。そもそも、失敗終了などの異常終了は、プログラムがそれ以上実行出来ない異常事態に陥った場合です。それが発覚するのは`main`関数の中というのはかなり希であり、その場で`exit`関数を呼び出すでしょう。つまり、`main`関数にそのような機能を付けたとしても`return 0;`以外を使うことはほとんどないから、付けなかったと思われます。極少ない例をとっても、`main`関数内で`exit`関数を呼び出してはいけないわけではありませんので、無くても誰も困らないと判断されたのでしょう。 Cが作られた当初のUNIXは、小さいプログラムをたくさん用意して、それを組み合わせて使うことが多かったというのもあると思います。小さいプログラムにおいて、`main`関数の戻り値で終了ステータスを表すというのは有用だったのかも知れません。しかし、現代は、プログラムは複雑高機能化する傾向にあり、時代にそぐわなくなったのだとも思われます。