C言語を学び始めると、教科書やサンプルコードに必ずと言ってよいほどreturn 0;が登場します。
しかし「とりあえず書けと言われたから書いているだけで、意味はよく分かっていない」という方も多いと思います。
本記事では、main関数の戻り値とreturn 0;の本当の意味を、C言語初心者の方にも分かりやすいように、順を追って丁寧に解説します。
main関数と戻り値(return 0;)の基本
main関数とは?C言語プログラムの入り口
C言語のプログラムは、必ず1つのmain関数から実行がスタートします。
どんなにたくさん関数を書いても、実際にOSが「ここから動かしなさい」と指定するのはmain関数だけです。
main関数の役割
C言語のプログラムを実行すると、次のような流れになります。
- OSが実行ファイルを読み込む
- OSが
main関数を呼び出す main関数の中の処理が上から順に実行されるreturnでmain関数が終了する- OSが
mainの戻り値を受け取る
この5番目の「OSが戻り値を受け取る」という部分が、return 0;の意味と深く関係してきます。
main関数は特殊な関数
C言語では、関数は一般に次のような形で定義します。
- 戻り値の型
- 関数名
- 引数
main関数も例外ではなく、戻り値の型を持つ「関数」です。
典型的には次のように書きます。
#include <stdio.h>
// C言語プログラムの「入り口」となる関数
int main(void) {
printf("Hello, world!\n");
return 0; // OSに「正常に終わりました」と伝える
}
ここでintと書いていることが、「main関数は整数型(int)の値を返す」という宣言になっています。
そして、その値を実際に返す命令がreturnです。
main関数の戻り値とは?OSへの「終了ステータス」
main関数が返す値は、OS(オペレーティングシステム)に伝えられる「終了ステータス」です。
終了ステータスは、「プログラムがどういう結果で終わったか」をOSや他のプログラムに知らせるための仕組みです。
終了ステータスのイメージ
終了ステータスは、たとえば次のような意味で使われます。
- 0 … 正常に終わった
- 0以外 … 何かエラーや異常があった
このルールはC言語の仕様そのものではなく、UNIXやWindowsなどのOSや、その文化で共有されている慣習です。
とはいえ、多くの言語やツールがこのルールを前提にしているため、Cでもそれに従うのが一般的です。
OSや他のプログラムから見た終了ステータス
終了ステータスは、次のような場面で利用されます。
- バッチ処理やシェルスクリプトからCプログラムを呼び出し、
0なら次の処理へ進み、0以外ならエラー処理を行う - 別のプログラムが、コマンドの成功・失敗を終了ステータスで判断する
- OSのイベントログやタスクスケジューラが結果を確認する
このように、main関数の戻り値は、プログラムの外側の世界とやり取りするための大切な情報なのです。
return 0;の意味と「正常終了」を表す理由
return 0;は、「main関数を終わらせて、OSに0という終了ステータスを返す」という意味を持ちます。
この0という値が「正常終了」を表します。
なぜ0が「正常」で0以外が「異常」なのか
これは歴史的・文化的な理由が大きいですが、実務では次のように理解しておくとよいです。
- 0は「問題なし」
- エラーが起きていない
- 期待した処理が最後まで完了した
- 0以外は「何らかの異常」
- 入力が不正だった
- ファイルを開けなかった
- メモリ確保に失敗した など
プログラムの外から見ると、「とりあえず0なら成功、0以外なら何か問題があった」と判断できます。
簡単なサンプルで確認
#include <stdio.h>
int main(void) {
printf("プログラムが正常に動作しました。\n");
// 正常終了を意味するreturn 0;
return 0;
}
このプログラムを実行すると、OSは終了ステータスとして0を受け取ります。
LinuxやmacOSのターミナルであれば、直後にecho $?と入力すると、直前のコマンドの終了ステータスを確認できます。
実行例(ターミナル上のイメージ)は次のようになります。
$ ./a.out
プログラムが正常に動作しました。
$ echo $?
0
return 0;とreturn 1;などの違い
return 0;の代わりにreturn 1;やreturn 2;などを書くこともできます。
値そのものの意味は、プログラマがどのように「エラーコード」として定義するかによります。
簡単な比較表
次の表は、典型的な使い分けの一例です。
| 戻り値 | 意味の例(よくある使い方) |
|---|---|
| 0 | 正常終了 |
| 1 | 使い方(引数)が間違っている |
| 2 | ファイルが開けないなど外部環境の問題 |
| 3 | メモリ不足など内部的な致命的エラー |
これはあくまで一例ですが、「0以外は何らかの失敗」という大枠を守りつつ、値ごとにエラーの種類を分けるという考え方です。
return 1;の簡単な例
#include <stdio.h>
int main(void) {
int ok = 0; // この例ではわざと「エラー」にしてみる
if (!ok) {
fprintf(stderr, "エラー: 条件を満たしていません。\n");
return 1; // エラー終了(1を返す)
}
printf("正常終了しました。\n");
return 0; // 正常終了(0を返す)
}
エラー: 条件を満たしていません。
このプログラムはreturn 1;で終わるので、終了ステータスは1になります。
return 0;がない場合と書くべき理由
returnを書かないmain関数はコンパイルエラーになる?
C言語初心者の方がよく疑問に思う点として、「return 0;を書かないとコンパイルエラーになるのか」というものがあります。
結論から言うと、最近のCコンパイラ(C99以降に準拠)では、多くの場合エラーにはなりません。
例: returnを書かないmain関数
#include <stdio.h>
int main(void) {
printf("returnを書いていませんが、どうなるでしょうか?\n");
// ここにreturn 0;を書いていない
}
このコードは、C99以降を標準とするコンパイラでは、しばしばコンパイルが通り、実行もできます。
その場合、コンパイラが自動的に「return 0;がある」とみなすためです。
ただし、コンパイラの設定やCのバージョン(C90など)によっては、警告が出たり、場合によってはエラー扱いになることもあります。
C99以降の仕様と「暗黙のreturn 0;」の注意点
C99以降のC言語仕様では、main関数から「処理が最後まで到達して抜けた場合」、return 0;を書いたのと同じ意味になると定められています。
これをよく「暗黙のreturn 0;」と呼びます。
暗黙のreturn 0;のルール
- 関数が
int main(void)またはint main(int argc, char *argv[])など、戻り値がintのmainであること - 関数の末尾まで到達し、そのまま抜けた場合
- そのとき、OSには
0が返されるとみなされる
この仕様のおかげで、短いサンプルコードなどではreturn 0;を書かなくても動くようになりました。
しかし、この仕組みには注意点があります。
注意点1: main以外では通用しない
他の関数では、戻り値を持つ関数でreturnを書かないと「未定義動作」になります。
#include <stdio.h>
// 戻り値intなのに、returnを書いていないNG例
int add(int a, int b) {
int c = a + b;
// return c; を書き忘れている
}
int main(void) {
int result = add(2, 3);
printf("結果: %d\n", result); // 何が表示されるか保証されない
return 0;
}
このようなコードは、コンパイルは通る場合もありますが、実行結果が保証されない危険なコードです。
mainだけ特別に「暗黙のreturn 0;」が許されていると考えてください。
注意点2: 可読性や習慣の面で問題が出る
暗黙のreturn 0;に頼ると、次のような問題が起きやすくなります。
- 他人がコードを読んだときに「どこで終わっているのか」一瞬分かりにくい
- C以外の言語を書くときに、戻り値を意識しなくなるクセがつく
- 終了ステータスの扱いを理解しないまま進んでしまう
プログラマ自身が「終了ステータス」という概念を理解するためにも、明示的にreturn 0;を書くことはとても大切です。
return 0;を書かないと困るケース
日常的な小さなプログラムでは、暗黙のreturn 0;でもあまり問題にならない場合もあります。
しかし、少し規模が大きくなったり、他のプログラムと連携する場面では、きちんと戻り値を書くことが重要になります。
困るケースの例
- シェルスクリプトから呼び出して、成功・失敗で分岐したいとき
- バッチ処理で「失敗したらメール通知」などをしたいとき
- CIツールやテストツールが、終了ステータスで結果を判断するとき
このような場合、正常終了は0、異常終了は0以外というルールを守っていないと、周辺ツールが正しく動かなくなる可能性があります。
C言語初心者が必ずreturn 0;を書くべき理由
C言語初心者であっても、main関数の最後にはreturn 0;を書くことを「習慣」にすることを強くおすすめします。
その理由としては、次のようなものがあります。
- 「関数は戻り値を返す」という基本ルールを体で覚えられる
- 終了ステータスの概念を早い段階で理解できる
- 他のプログラミング言語(Python、Java、C++など)でも「終了コード」の考え方を応用しやすくなる
- チーム開発や実務で書くコードに近いスタイルになる
特に、「サンプルコードだから省略してもいいや」ではなく、「サンプルコードだからこそ正しい形で書く」という意識が重要です。
main関数の書き方と実装例
基本形のmain関数(int main(void)とreturn 0;)
まずは、C言語で一般的な基本形のmain関数を確認しておきます。
#include <stdio.h>
// Cプログラムの基本的なmain関数の形
int main(void) {
printf("こんにちは、C言語!\n");
// 正常終了を表す
return 0;
}
この形は、次の点で初心者にとって分かりやすい書き方です。
int main(void)で「引数なし」「戻り値はint」と宣言している- 最後に
return 0;があり、「正常終了」を明示している
他にも、コマンドライン引数を受け取るための次のような形もよく使われます。
int main(int argc, char *argv[]) {
// argc: 引数の個数
// argv: 引数の文字列配列
return 0;
}
初心者のうちはint main(void)をメインで使い、コマンドライン引数を学ぶ段階になったらint main(int argc, char *argv[])の形を覚えるとよいです。
WindowsとLinuxでのmain関数と戻り値の扱い
WindowsとLinux(UNIX系)でも、基本的な考え方は同じです。
どちらでも、mainの戻り値は終了ステータスとしてOSに返されます。
Linux/UNIX系の場合
LinuxやmacOSのターミナルでは、前述のようにecho $?で終了ステータスを確認できます。
$ ./myprog
$ echo $?
0
Windows(コマンドプロンプト)の場合
Windowsのコマンドプロンプトでは、echo %ERRORLEVEL%で終了コードを確認できます。
想定される実行例:
C:\> myprog.exe
C:\> echo %ERRORLEVEL%
0
どちらの環境でも、0は成功、0以外はエラーという扱いをするツールやスクリプトが多いため、このルールを守っておくことが重要です。
エラー時に0以外を返すサンプルコード
ここでは、引数の個数が正しいかどうかをチェックし、間違っていたら1を返すサンプルを示します。
これにより、return 0;とreturn 1;の使い分けがイメージしやすくなります。
#include <stdio.h>
// コマンドライン引数を1つだけ受け取るプログラム
int main(int argc, char *argv[]) {
if (argc != 2) {
// 標準エラー出力に使い方を表示する
fprintf(stderr, "使い方: %s 名前\n", argv[0]);
// 引数の個数が間違っているのでエラーとして1を返す
return 1;
}
// 正しい使い方: 名前を1つだけ受け取る
printf("こんにちは、%sさん!\n", argv[1]);
// 正常に処理ができたので0を返す
return 0;
}
想定される実行例:
$ ./greet
使い方: ./greet 名前
$ echo $?
1
$ ./greet Taro
こんにちは、Taroさん!
$ echo $?
0
このように、エラー状況ごとに異なる数値を返すことで、呼び出し元が原因を判別しやすくなります。
戻り値を使ってバッチ処理やシェルスクリプトと連携する例
main関数の戻り値は、バッチ処理やシェルスクリプトと連携するときに特に威力を発揮します。
ここではLinux系シェルを例に、簡単な連携例を示します。
Cプログラム側の例
#include <stdio.h>
#include <stdlib.h>
// 引数で与えられた整数が0以上なら成功、負ならエラーとする例
int main(int argc, char *argv[]) {
if (argc != 2) {
fprintf(stderr, "使い方: %s 数値\n", argv[0]);
return 1; // 使い方エラー
}
int value = atoi(argv[1]);
if (value < 0) {
fprintf(stderr, "エラー: 負の値は受け付けません。\n");
return 2; // 値が不正
}
printf("値は %d です。\n", value);
return 0; // 正常終了
}
シェルスクリプト側の例(bash風)
以下はイメージ用のシェルスクリプト例です。
C言語初心者の方は、ざっくりと「Cプログラムの終了コードで分岐している」と理解していただければ十分です。
#!/bin/sh
./check_value "$1"
status=$? # 直前のコマンドの終了ステータスを取得
if [ $status -eq 0 ]; then
echo "Cプログラムは正常終了しました。"
elif [ $status -eq 1 ]; then
echo "Cプログラムの使い方が間違っています。"
elif [ $status -eq 2 ]; then
echo "不正な値が指定されました。"
else
echo "想定外のエラーが発生しました。(コード: $status)"
fi
このように、main関数の戻り値は「外の世界」と連携するためのインターフェースとして活用できます。
main関数の戻り値を正しく使うコツ
正常終了は0、異常終了は0以外にするルール
まず最初に覚えるべき重要なルールは、「正常終了は0、異常終了は0以外」という約束です。
これはC言語だけでなく、多くの言語やツールで共通している慣習です。
実務でコードを書くときも、次のような方針で整理すると分かりやすくなります。
- 成功パターンは必ず
return 0; - エラーが発生したら
return 1;以上の値 - エラーの種類ごとに値を変えると、さらに親切
こうしておくことで、後からシェルスクリプトやバッチで扱うときにも混乱しにくくなります。
エラーコードを定数やenumで管理する方法
エラーが増えてくると、return 1;やreturn 2;といった「数字だけ」が並んでいても、何のエラーなのか分かりにくくなります。
そこで、定数やenumを使って名前を付けて管理する方法がよく使われます。
#defineで定数として定義する例
#include <stdio.h>
#include <stdlib.h>
#define ERR_ARGS 1 // 引数のエラー
#define ERR_RANGE 2 // 範囲外の値
#define ERR_OTHER 3 // その他のエラー
int main(int argc, char *argv[]) {
if (argc != 2) {
fprintf(stderr, "使い方: %s 0〜100の数値\n", argv[0]);
return ERR_ARGS;
}
int value = atoi(argv[1]);
if (value < 0 || value > 100) {
fprintf(stderr, "エラー: 0〜100の範囲で指定してください。\n");
return ERR_RANGE;
}
printf("有効な値です: %d\n", value);
return 0; // 正常終了
}
このようにすると、コードを読んだときに「どんなエラーなのか」がすぐに分かるようになります。
enumを使った例
Cではenumを使って、次のようにまとめて定義することもできます。
#include <stdio.h>
#include <stdlib.h>
typedef enum {
EXIT_OK = 0, // 正常終了
EXIT_ARGS = 1, // 引数エラー
EXIT_RANGE = 2, // 範囲エラー
EXIT_INTERNAL = 3 // 内部エラー
} ExitCode;
int main(int argc, char *argv[]) {
if (argc != 2) {
fprintf(stderr, "使い方: %s 0〜100の数値\n", argv[0]);
return EXIT_ARGS;
}
int value = atoi(argv[1]);
if (value < 0 || value > 100) {
fprintf(stderr, "エラー: 0〜100の範囲で指定してください。\n");
return EXIT_RANGE;
}
printf("有効な値です: %d\n", value);
return EXIT_OK;
}
戻り値に意味のある名前を付けることで、コードの意図が格段に読みやすくなることが分かると思います。
小さなプログラムでもreturn 0;を書く習慣をつけるコツ
最後に、C言語初心者の方が「どんなに小さなプログラムでもreturn 0;を書く習慣」を身につけるためのコツをいくつか紹介します。
1. テンプレートを決めてしまう
毎回次のような「ひな型」を使うようにします。
#include <stdio.h>
int main(void) {
// ここに処理を書く
return 0; // 最後に必ず書く
}
エディタやIDEの「スニペット機能」を使って、このテンプレートをすぐに挿入できるようにすると便利です。
2. 「終点」を必ず意識する
コードを書くときに、「この関数はどこで終わるのか」「終わるときに何を返すのか」を意識する習慣をつけます。
main関数でも同じで、「ここで終わり」という印としてreturn 0;を書く、と考えるとよいです。
3. 学習段階からエラー時のreturnも書いてみる
簡単なプログラムであっても、あえて次のように「エラーの場合は別の値を返す」ようにしてみると、戻り値の意味が身につきやすくなります。
#include <stdio.h>
int main(void) {
int ok = 1; // 条件がOKかどうか
if (!ok) {
// 何か問題があったと想定して1を返す
return 1;
}
// 問題なければ0を返す
return 0;
}
このように、学習の早い段階から「値を返す」という感覚を養うと、C言語の他の関数設計にも良い影響が出ます。
まとめ
本記事では、「return 0;はなぜ書くのか」「main関数の戻り値にはどんな意味があるのか」という疑問を中心に、C言語初心者向けに丁寧に解説しました。
ポイントを整理すると、次のようになります。
- main関数はCプログラムの入り口であり、戻り値はOSへの「終了ステータス」です
return 0;は「正常終了」を意味し、0以外は「異常終了」を表すのが一般的なルールです- C99以降では
mainの末尾まで到達すると「暗黙のreturn 0;」になりますが、明示的にreturn 0;を書く習慣をつけた方が安全で理解もしやすいです - エラーの種類に応じて
1、2などを返すことで、シェルスクリプトやバッチ処理と連携しやすくなります - エラーコードは
#defineやenumで名前を付けて管理すると、コードの意味が明確になります - どんなに小さなプログラムでも、mainの最後に
return 0;を書くことを、学習初期から習慣にするのがおすすめです
「とりあえず書くもの」だったreturn 0;が、「OSや他のプログラムに結果を伝えるための大事なメッセージ」であることが理解できれば、C言語でのプログラミングが一段とおもしろく、実践的に感じられるはずです。
今後は、main関数の戻り値を意識しながら、少しずつ複雑なプログラムにも挑戦してみてください。
