C言語のきほん|main関数の返却値と引数

main関数の形がわかると、Cプログラムの入口と出口がはっきり見えてくる

C言語の学習を始めると、ほとんどのプログラムで最初に目にするのが main 関数です。
これまで何度も

int main(void)

という形を書いてきたと思いますが、学習の初期段階では「とりあえずこう書くもの」と覚えていた人も多いかもしれません。

でも、この形にはちゃんと意味があります。
main 関数は、ただ最初に実行される関数というだけではなく、プログラムの開始地点であり、同時にプログラムの終了結果を外側へ返す役割も持っています。

さらに、main 関数は引数を受け取らない形だけでなく、外部から値を受け取る形で書くこともできます。
つまり main 関数も、これまで学んできた「引数」と「返却値」の考え方の上に成り立っている関数だといえます。

この内容を理解すると、

  • なぜ main の返却値型が int なのか
  • なぜ main の引数が void なのか
  • return 0; は何を意味しているのか
  • main 関数も引数を受け取れるとはどういうことか

といった点がすっきり整理できます。

ここでは、main 関数の返却値と引数に注目しながら、Cプログラムの入口と出口のしくみをやさしく確認していきましょう。

main関数の基本の形

これまでよく見てきた main 関数の形は、次のようなものです。

int main(void)
{
    return 0;
}

この1行には、大きく分けて2つの意味があります。

要素書かれているもの意味
返却値型intプログラムの終了結果を返す
引数void外部から引数を受け取らない

つまりこの main 関数は、引数は受け取らず、整数値を返す関数です。

普段は main 関数を「特別なもの」として見がちですが、実はこれも関数の1つです。
ただし、プログラムの開始地点になるという特別な役割を持っています。

返却値型が int である意味

main 関数の先頭には int が書かれています。
これは、main 関数が int 型の値を返す ことを表しています。

では、その値は誰に返しているのでしょうか。
それは、オペレーティングシステム OS です。

プログラムは単独で動いているように見えても、実際には OS の上で実行されています。
そしてプログラムが終了するときには、「正常に終わったか」「何か問題があって終わったか」という結果を OS に返します。
そのために使われるのが、main 関数の返却値です。

よく使う返却値

返却値意味
0正常終了
0以外異常終了、何らかのエラー

そのため、学習用のプログラムではよく最後に次のように書きます。

return 0;

これは、このプログラムは正常に終わりました と OS に伝えているわけです。

なぜ return 0; を書くのか

学習を始めたばかりの頃は、return 0; を「とりあえず書くお決まり」として見てしまいがちです。
でも、ここにはちゃんと役割があります。

たとえば次の main 関数を見てみましょう。

int main(void)
{
    printf("処理を開始します。\n");
    return 0;
}

この return 0; は、main 関数を終わらせるだけでなく、終了ステータス 0 を OS に返す という意味を持っています。

これまで学んだ「値を返す関数」の考え方と同じように、main 関数も値を返しているのです。
ただし、普通の関数が呼び出し元に値を返すのに対して、main 関数は OS へ返している 点が少し特別です。

引数が void である意味

main 関数の丸かっこの中には void が書かれています。

int main(void)

この void は、引数を受け取らない ことを表しています。

つまり、この形の main 関数では、プログラム実行時に外部から特別な値を受け取らずに処理を始める、ということです。

ここでも、void は「何もない」という意味で使われています。

位置意味
先頭の intint 型の値を返す
丸かっこの中の void引数を受け取らない

この2つは役割が違うので、混同しないことが大切です。

main関数も引数を受け取れる

普段よく見るのは int main(void) ですが、main 関数は引数を受け取る形で書くこともできます。
これは、プログラム実行時に外部から情報を受け取りたいときに使います。

たとえば、コマンドラインから文字列を渡して、その内容に応じて処理を変えるようなプログラムです。

この詳しい書き方は別の節で学ぶ内容ですが、ここではまず、

main 関数も、場合によっては引数を持つ関数として定義できる

ということを知っておくのが大切です。

つまり main 関数も、これまで学んできた関数のルールから完全に外れているわけではなく、返却値と引数を持つ関数の1つとして考えられます。

main関数を普通の関数と比べてみる

main 関数の意味をつかみやすくするために、普通の関数と比べてみましょう。

項目普通の関数main 関数
呼び出され方別の関数から呼ばれるプログラム開始時に実行される
返却値の戻り先呼び出し元の関数OS
引数あってもなくてもよいあってもなくてもよい
役割部分的な処理を担当プログラム全体の入口

この表からも分かるように、main 関数は特別な役割を持っていますが、文法の考え方そのものは普通の関数と共通しています。

サンプルプログラム

ここでは、main 関数の返却値型と引数に注目しながら、シンプルなサンプルプログラムを見てみます。
内容は、画面にメッセージを表示して正常終了するだけの簡単なものにします。

ファイル名:13_9_1.c

// main関数の返却値と引数を確認するプログラム
#include <stdio.h>

int main(void)
{
    printf("今日もC言語の学習を進めましょう。\n");

    return 0;
}

実行結果例

今日もC言語の学習を進めましょう。

このプログラムでは、main 関数に引数はありません。
だから丸かっこの中は void です。

そして最後に return 0; があるので、このプログラムは正常終了したことを OS に伝えます。

このサンプルの見どころ

このプログラムは短いですが、main 関数の基本がしっかり入っています。

コード意味
int main(void)引数なし、返却値は int
printf(...)画面にメッセージを表示する
return 0;正常終了を OS に伝える

短いプログラムほど、main 関数の形に注目しやすいです。
処理内容よりも、入口としての main出口としての return 0; を意識して読むと理解しやすくなります。

図で考えるとわかりやすい

main 関数の返却値は、ほかの関数の返却値とは少し違って見えやすいので、図で流れをイメージすると分かりやすいです。

この図では、main 関数がプログラムの中心にあり、最後に return 0 を OS へ返している流れを表しています。

普通の関数では返却値は呼び出し元の関数へ戻りますが、main 関数では戻り先が OS です。
ここが main 関数らしい特徴です。

また、丸かっこの中に void があるので、今回は外部から引数を受け取らない形で始まっていることも分かります。

返却値が 0 以外のときはどうなるのか

基本としては、0 は正常終了です。
では 0 以外ならどう考えるのでしょうか。

一般には、0 以外の値は異常終了や何らかの問題があったことを示す値として使われます。
たとえば、入力エラーやファイルが開けなかった場合などに、エラー番号のように使うことがあります。

たとえば次のようなイメージです。

int main(void)
{
    int data = -1;

    if (data < 0) {
        return 1;
    }

    return 0;
}

この例では、問題があれば 1 を返し、問題がなければ 0 を返しています。

学習の初期段階では、まずは

  • 0 は正常終了
  • 0以外は異常終了

と押さえておけば十分です。

main関数の引数が必要になる場面

今回は int main(void) という形を中心に見ていますが、main 関数が引数を受け取る場面もあります。
それは、プログラム実行時に外部から情報を渡したいときです。

たとえば、コマンドラインで次のように実行する場面を考えると分かりやすいです。

program sample.txt

このように、プログラム名のあとにファイル名などを付けて実行する場合、main 関数側でそれを受け取る必要があります。
そのため、引数付きの main 関数が使われます。

今の段階では細かな書き方までは覚えなくても大丈夫ですが、
main 関数も状況によっては引数を持てる
ということを知っておくと、あとで学ぶ内容につながりやすくなります。

main関数の書き方を見分けるポイント

main 関数を見るときは、次の2つに注目すると意味がつかみやすいです。

注目する場所確認すること
関数名の前何型を返すか
丸かっこの中何を受け取るか

たとえば、

int main(void)

なら、

  • int → 整数を返す
  • void → 引数なし

です。

これは、これまで学んだ関数宣言の読み方と同じです。
つまり main 関数も、特別すぎるものとして切り離すのではなく、関数の基本ルールに従って読むのがコツです。

実践問題

次の仕様に従って関数を作成し、main 関数から呼び出して結果を確認してください。

関数宣言

void print_lucky_message(void);

機能
1〜5 の範囲の乱数を使って、今日のラッキーナンバーを表示する。

返却値
なし

補足
rand と srand を使ってください。

実行結果例

今日のラッキーナンバーは4です。

print_lucky_message 関数で表示
main 関数は正常終了として 0 を返すこと

解答例

ファイル名:13_9_2.c

// ラッキーナンバーを表示するプログラム
#include <stdio.h>
#include <stdlib.h>
#include <time.h>

void print_lucky_message(void);

int main(void)
{
    srand((unsigned int)time(NULL));

    print_lucky_message();

    return 0;
}

// 今日のラッキーナンバーを表示する関数
void print_lucky_message(void)
{
    int number = rand() % 5 + 1;

    printf("今日のラッキーナンバーは%dです。\n", number);
}

解答例の解説

この問題では、関数 print_lucky_message 自体は返却値を持ちません。
そのため、関数宣言は

void print_lucky_message(void);

となっています。

一方で、main 関数は

int main(void)

となっていて、最後に

return 0;

を書いています。

ここでのポイントは、呼び出した関数が void でも、main 関数は int を返すということです。
つまり、main 関数自身の返却値と、main から呼び出す別の関数の返却値は分けて考える必要があります。

今回の流れは次のとおりです。

順番内容
1main 関数が始まる
2srand で乱数の準備をする
3print_lucky_message を呼び出す
4関数の中でラッキーナンバーを表示する
5main 関数へ戻る
6return 0; で OS へ正常終了を返す

このように、main 関数はプログラム全体の流れを管理する立場として見ると理解しやすいです。

実践問題

天気と時間帯でメッセージを表示する関数を作ります。

次の仕様に従って関数を作成し、main 関数から呼び出して結果を確認してください。

関数宣言

void weather_message(int weather, int period);

機能
天気 weather と時間帯 period を引数として受け取り、メッセージを表示する。

返却値
なし

補足

  • 天気
    1:晴れ
    2:くもり
    3:雨
  • 時間帯
    1:朝
    2:昼
    3:夜

表示内容は自由に条件分岐で決めてよいものとする。

解答例

ファイル名:13_9_3.c

// 天気と時間帯に応じたメッセージを表示するプログラム
#include <stdio.h>

void weather_message(int weather, int period);

int main(void)
{
    int weather;
    int period;

    printf("天気を入力してください(1:晴れ 2:くもり 3:雨) > ");
    scanf("%d", &weather);

    printf("時間帯を入力してください(1:朝 2:昼 3:夜) > ");
    scanf("%d", &period);

    printf("\n");

    weather_message(weather, period);

    return 0;
}

// 天気と時間帯に応じたメッセージを表示する関数
void weather_message(int weather, int period)
{
    if (weather == 1 && period == 1) {
        printf("気持ちのよい朝ですね。\n");
    }
    else if (weather == 1 && period == 2) {
        printf("明るい昼になりそうです。\n");
    }
    else if (weather == 1 && period == 3) {
        printf("星が見える夜かもしれません。\n");
    }
    else if (weather == 2) {
        printf("空模様を見ながら過ごしましょう。\n");
    }
    else if (weather == 3) {
        printf("雨具を忘れずにしてください。\n");
    }
    else {
        printf("入力値を確認してください。\n");
    }
}

実践問題

年と月を受け取って、その月の日数を表示する少し軽めの実践問題です。

次の仕様に従って関数を作成し、main 関数から呼び出して結果を確認してください。

関数宣言

void print_days_in_month(int year, int month);

機能
年 year と月 month を引数として受け取り、その月の日数を表示する。

返却値
なし

補足
2月は閏年を考慮すること。

実行結果例

西暦を入力してください > 2024
月を入力してください > 2

2024年2月は29日まであります。

解答例

ファイル名:13_9_4.c

// 指定した月の日数を表示するプログラム
#include <stdio.h>

void print_days_in_month(int year, int month);
int is_leap_year(int year);

int main(void)
{
    int year;
    int month;

    printf("西暦を入力してください > ");
    scanf("%d", &year);

    printf("月を入力してください > ");
    scanf("%d", &month);

    printf("\n");

    print_days_in_month(year, month);

    return 0;
}

// 閏年かどうかを判定する関数
int is_leap_year(int year)
{
    if (year % 400 == 0) {
        return 1;
    }
    else if (year % 100 == 0) {
        return 0;
    }
    else if (year % 4 == 0) {
        return 1;
    }
    else {
        return 0;
    }
}

// 指定した月の日数を表示する関数
void print_days_in_month(int year, int month)
{
    int days;

    if (month < 1 || month > 12) {
        printf("月は1〜12の範囲で入力してください。\n");
        return;
    }

    if (month == 2) {
        if (is_leap_year(year) == 1) {
            days = 29;
        }
        else {
            days = 28;
        }
    }
    else if (month == 4 || month == 6 || month == 9 || month == 11) {
        days = 30;
    }
    else {
        days = 31;
    }

    printf("%d年%d月は%d日まであります。\n", year, month, days);
}

学習のコツ

main 関数の返却値と引数を理解するときは、次の見方をすると整理しやすいです。

main も関数の1つとして読む

特別扱いしすぎず、まずは
返却値の型は何か
引数は何か
といういつもの見方で読むと理解しやすくなります。

戻り先が OS であることを意識する

main 関数の返却値は、ほかの関数のように別の関数へ戻るのではなく、OS へ返されます。
ここがいちばん大事な特徴です。

return 0; の意味を言葉で説明できるようにする

単なるお決まりではなく、
「正常終了を OS に知らせている」
と説明できるようになると、main 関数への理解がぐっと深まります。