6日でできる 新C言語入門|引数・戻り値・void関数の基本

関数は、値を受け取ることも、結果を返すことも、表示だけを担当することもできる。
引数・戻り値・void関数の違いを理解して、目的に合った関数を作れるようになろう。

C言語で関数を使うと、処理を小さな部品に分けて整理できます。前回までに、関数を作ることで同じ処理を何度も使えることや、プロトタイプ宣言によって関数の定義順序を整理できることを学びました。

ここでさらに大切になるのが、関数にはいくつかの種類があるという考え方です。

関数には、値を受け取って計算結果を返すものがあります。たとえば、2つの点数を比較して高い方を返す関数です。
また、値は受け取るけれど結果は返さず、画面に表示するだけの関数もあります。
さらに、値を受け取らず、結果も返さず、決まった表示だけを行う関数もあります。

この違いを理解するために重要なのが、引数、戻り値、voidです。

引数は、関数に渡す値です。戻り値は、関数から呼び出し元へ返す結果です。voidは、戻り値がない、または引数がないことを表すときに使います。

この記事では、引数あり・戻り値ありの関数、引数あり・戻り値なしの関数、引数なし・戻り値なしの関数を中心に、return文の使い方やローカル変数のスコープまで、サンプルプログラムを通してやさしく解説していきます。

関数にはいろいろな形がある

C言語の関数は、すべて同じ形ではありません。関数が何をするのかによって、引数や戻り値の有無が変わります。

たとえば、計算結果を求めたい場合は、関数から結果を返す必要があります。一方、画面にメッセージを表示するだけなら、戻り値は必要ありません。

代表的な関数の形を整理すると、次のようになります。

関数の形引数戻り値向いている処理
引数あり・戻り値ありありあり計算して結果を返す
引数あり・戻り値なしありなし値を受け取って表示や処理を行う
引数なし・戻り値なしなしなし決まった表示や区切り線を出す
引数なし・戻り値ありなしあり決まった値を返す

今回の記事では、特に使う機会が多い3つの形を中心に見ていきます。

引数と戻り値の考え方

引数は、関数に渡す値です。関数の外から、関数の中へデータを渡すために使います。

たとえば、次の関数は2つの整数を受け取ります。

int maxScore(int a, int b)

この場合、aとbが引数です。関数を呼び出す側では、次のように値を渡します。

maxScore(score1, score2)

戻り値は、関数の処理結果として返ってくる値です。

return a;

このようにreturnを書くと、その値が呼び出し元へ返されます。

用語意味
引数関数へ渡す値score1, score2
戻り値関数から返ってくる値高い方の点数
void値がないことを表す戻り値なし、引数なし

引数は関数への入力、戻り値は関数からの出力と考えると分かりやすいです。

図:引数と戻り値は関数の入力と出力

この図から分かること

この図から分かるのは、関数には値を受け取る入口と、結果を返す出口があるということです。

引数は関数へ渡す値であり、関数の中で計算や判定に使われます。戻り値は、関数の処理結果として呼び出し元へ返される値です。計算する関数では、引数と戻り値の流れを意識すると理解しやすくなります。

3種類の関数を使い分けるプログラム

次のプログラムでは、3種類の関数を使っています。

1つ目は、2つの点数のうち高い方を返す関数です。これは引数あり・戻り値ありの関数です。
2つ目は、受け取った点数を表示する関数です。これは引数あり・戻り値なしの関数です。
3つ目は、区切り線を表示する関数です。これは引数なし・戻り値なしの関数です。

プロジェクト/ファイル名: Lesson39_1/main.c

#include <stdio.h>

// プロトタイプ宣言
int maxScore(int, int);
void showScore(int);
void printLine(void);

int main(void) {
    int score1 = 86;
    int score2 = 72;
    int higher;

    // 区切り線を表示する
    printLine();

    // 2つの点数を表示する
    showScore(score1);
    showScore(score2);

    // 2つの点数のうち高い方を求める
    higher = maxScore(score1, score2);

    printf("高い方の点数は %d 点です。\n", higher);

    // 区切り線を表示する
    printLine();

    return 0;
}

// 2つの整数のうち、大きい方を返す関数
int maxScore(int a, int b) {
    if (a > b) {
        return a;
    }

    return b;
}

// 受け取った点数を表示する関数
void showScore(int score) {
    printf("点数: %d 点\n", score);

    return;
}

// 区切り線を表示する関数
void printLine(void) {
    printf("--------------------\n");
}

実行結果

--------------------
点数: 86 点
点数: 72 点
高い方の点数は 86 点です。
--------------------

このプログラムでは、先頭に3つのプロトタイプ宣言を書いています。

int maxScore(int, int);
void showScore(int);
void printLine(void);

これにより、main関数より後ろに関数本体が書かれていても、main関数の中でそれぞれの関数を呼び出せます。

それぞれの関数の役割を整理する

Lesson39_1/main.cで使っている関数を整理すると、次のようになります。

関数名引数戻り値役割
maxScoreint型を2つint型2つの点数のうち高い方を返す
showScoreint型を1つなし受け取った点数を表示する
printLineなしなし区切り線を表示する

同じ関数でも、目的によって形が変わります。

maxScoreは計算結果を使いたいので、戻り値があります。
showScoreは画面に表示することが目的なので、戻り値はありません。
printLineは決まった区切り線を表示するだけなので、引数も戻り値もありません。

このように、関数を作るときは、関数に何を渡す必要があるか、関数から何を受け取りたいかを考えることが大切です。

引数あり・戻り値ありの関数

maxScore関数は、引数あり・戻り値ありの関数です。

int maxScore(int a, int b) {
    if (a > b) {
        return a;
    }

    return b;
}

この関数は、aとbという2つの整数を受け取ります。そして、どちらが大きいかを判定し、大きい方を戻り値として返します。

関数名の前にあるintは、この関数がint型の値を返すことを表しています。

int maxScore(int a, int b)

main関数では、次のように呼び出しています。

higher = maxScore(score1, score2);

この呼び出しでは、score1とscore2の値がmaxScore関数へ渡されます。関数の戻り値はhigherに代入されます。

計算結果を呼び出し元で使いたい場合は、このように戻り値のある関数にすると便利です。

return文は値を返して関数を終了する

return文は、関数から値を返すときに使います。また、returnが実行されると、その関数の処理はそこで終了します。

maxScore関数をもう一度見てみましょう。

int maxScore(int a, int b) {
    if (a > b) {
        return a;
    }

    return b;
}

aがbより大きい場合は、return a;が実行されます。この時点で関数は終了するため、その下のreturn b;は実行されません。

aがbより大きくない場合は、if文の中に入らず、最後のreturn b;が実行されます。

条件実行されるreturn文返される値
a > b が真return a;a
a > b が偽return b;b

このように、returnは関数の最後だけでなく、条件分岐の途中でも使えます。条件によって早めに結果を返したい場合に便利です。

図:return文は値を返して関数を終了する

この図から分かること

この図から分かるのは、return文が値を返すだけでなく、関数の処理を終了させる役割も持っているということです。

条件に応じて早めにreturnすると、不要な処理を続けずに関数を終えられます。戻り値のある関数では、どの条件でも適切な値を返せるように書くことが大切です。

引数あり・戻り値なしの関数

showScore関数は、引数あり・戻り値なしの関数です。

void showScore(int score) {
    printf("点数: %d 点\n", score);

    return;
}

この関数は、scoreというint型の値を受け取ります。そして、その値を画面に表示します。

戻り値の型にはvoidが使われています。

void showScore(int score)

voidは、戻り値がないことを表しています。つまり、showScore関数は何かを計算して呼び出し元へ返すのではなく、受け取った値を表示することが目的です。

main関数では、次のように呼び出しています。

showScore(score1);
showScore(score2);

score1やscore2の値を関数へ渡すことで、同じ表示処理を繰り返し使えます。

void関数のreturnは省略できることがある

戻り値がない関数でも、return;を書くことがあります。

void showScore(int score) {
    printf("点数: %d 点\n", score);

    return;
}

この場合のreturn;は、値を返すためではなく、関数を終了するためのものです。戻り値がないため、returnの後ろには値を書きません。

ただし、関数の最後にあるreturn;は省略できます。

void showScore(int score) {
    printf("点数: %d 点\n", score);
}

どちらの書き方でも、関数の最後まで実行されると呼び出し元へ戻ります。

一方、条件によって途中で関数を抜けたい場合は、戻り値がない関数でもreturn;を使うことがあります。

void showScore(int score) {
    if (score < 0) {
        return;
    }

    printf("点数: %d 点\n", score);
}

この例では、scoreが0未満の場合、表示せずに関数を終了します。

引数なし・戻り値なしの関数

printLine関数は、引数なし・戻り値なしの関数です。

void printLine(void) {
    printf("--------------------\n");
}

この関数は、外から値を受け取りません。そして、呼び出し元へ値も返しません。

関数名の前にあるvoidは、戻り値がないことを表します。

void printLine

また、丸かっこの中のvoidは、引数がないことを表します。

printLine(void)

このような関数は、決まった表示をしたいときに便利です。区切り線、タイトル、案内メッセージなど、毎回同じ処理を行う場合に使いやすいです。

main関数では、次のように呼び出しています。

printLine();

引数がないため、丸かっこの中には何も書きません。

voidの意味を整理する

voidは、C言語の関数でよく使われる重要なキーワードです。

関数の戻り値の型として使う場合は、戻り値がないことを表します。

void showScore(int score)

関数の引数部分に使う場合は、引数がないことを表します。

void printLine(void)

表に整理すると、次のようになります。

書き方意味
void showScore(int score)戻り値なし、int型の引数あり
void printLine(void)戻り値なし、引数なし
int maxScore(int a, int b)int型の戻り値あり、int型の引数2つ

voidは、何もないことを明示するための言葉です。戻り値がないのか、引数がないのか、どこに書かれているかによって意味が変わります。

図:voidは戻り値なし・引数なしを表す

この図から分かること

この図から分かるのは、voidが書かれている場所によって意味が変わるということです。

関数名の前にあるvoidは、関数が値を返さないことを表します。丸かっこの中にあるvoidは、関数が値を受け取らないことを表します。戻り値と引数を区別して読むと、関数の形が理解しやすくなります。

関数の形を選ぶときの考え方

関数を作るときは、まず次の2つを考えると整理しやすくなります。

考えること判断のしかた
関数に渡す値があるかあるなら引数を使う
関数から結果を受け取りたいか受け取りたいなら戻り値を使う

たとえば、2つの点数を比べて高い方を求めたい場合は、2つの値を渡す必要があります。そして、結果として高い方の点数を受け取りたいので、戻り値も必要です。

int maxScore(int a, int b)

一方、点数を画面に表示するだけなら、点数を渡す必要はありますが、結果を返す必要はありません。

void showScore(int score)

区切り線を表示するだけなら、外から値を受け取る必要も、結果を返す必要もありません。

void printLine(void)

このように、関数の目的に合わせて、引数と戻り値を選びます。

変数のスコープとは

関数を学ぶときは、変数のスコープも大切です。

スコープとは、変数を使える範囲のことです。関数の中で宣言した変数は、その関数の中でだけ使えます。このような変数をローカル変数と呼びます。

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

void showLocalValue(void) {
    int localValue = 100;

    printf("関数内の値: %d\n", localValue);
}

localValueはshowLocalValue関数の中で宣言されています。そのため、showLocalValue関数の中では使えますが、main関数や別の関数から直接使うことはできません。

int main(void) {
    printf("%d\n", localValue);

    return 0;
}

このように別の関数からlocalValueを直接使おうとすると、localValueがその場所では見えないためエラーになります。

ローカル変数は関数の中だけで使える

ローカル変数は、その関数専用の変数です。

たとえば、main関数の中にscoreという変数があり、showScore関数の引数にもscoreという名前がある場合でも、それらは別の変数として扱われます。

void showScore(int score) {
    printf("点数: %d 点\n", score);
}

int main(void) {
    int score = 86;

    showScore(score);

    return 0;
}

main関数のscoreと、showScore関数のscoreは、同じ名前でも別の場所にある変数です。

この仕組みによって、関数ごとに変数を独立して扱えます。関数の中だけで必要な値は、その関数の中にローカル変数として用意すると、他の関数に影響しにくくなります。

スコープを意識するとバグを防ぎやすい

スコープを意識すると、変数の使い間違いを防ぎやすくなります。

たとえば、ある関数の中だけで使う一時的な値を、ほかの関数から直接変更できてしまうと、どこで値が変わったのか分かりにくくなります。

ローカル変数を使うと、変数の有効範囲が関数内に限定されます。そのため、関数ごとに処理を安全に分けやすくなります。

変数の種類使える範囲特徴
ローカル変数宣言した関数の中だけ他の関数から直接使えない
引数関数の中だけ呼び出し時に渡された値を受け取る
戻り値呼び出し元へ返す値関数の結果を外へ渡せる

関数の中で使う値はローカル変数や引数として扱い、外へ返したい値は戻り値として返す。この流れを意識すると、関数の設計が分かりやすくなります。

引数・戻り値・voidを使い分けると関数が書きやすくなる

関数には、引数や戻り値の有無によってさまざまな形があります。

値を受け取って結果を返す関数は、計算や判定に向いています。たとえば、maxScoreのように2つの値を比べて結果を返す関数です。

値を受け取るけれど結果を返さない関数は、表示処理に向いています。たとえば、showScoreのように受け取った点数を表示する関数です。

値を受け取らず結果も返さない関数は、決まった処理をまとめるときに便利です。たとえば、printLineのように区切り線を表示する関数です。

voidは、戻り値がないことや、引数がないことを表すために使います。関数名の前にあるvoidは戻り値なし、丸かっこの中にあるvoidは引数なしという意味になります。

また、関数の中で宣言した変数は、その関数の中でだけ使えるローカル変数です。スコープを意識することで、変数の使い間違いや思わぬバグを防ぎやすくなります。

関数を作るときは、この関数には何を渡す必要があるか、この関数から何を返したいかを考えると、引数・戻り値・voidの使い分けが自然にできるようになります。