苦しんで覚えるC言語 11.2 関数に数値を渡す P204 – P211

引数を持つ関数

これまで解説してきた関数はvoidと記載をして、関数には値を渡す処理をしてきませんでした。
ここでは、関数が処理に使う値を渡す方法を解説したいと思います。

#include <stdio.h>

int sum(int);  /* プロトタイプ宣言 */

int main(void)
{
    return 0;
}

int sum(int max)
{
    printf("%d\n", (1 + max) * max / 2);

    return 0;
}

上記は参考書のコードになります。
以前同様にこのままでは動作しません、、、
main関数からプログラムが開始するものの、return 0; で処理終了になります。

その点は目をつぶって、関数の解説をしたいと思います。

 int sum(int max)

最左のintはこれまで同様に、関数の処理が終了した際に呼び出し元に戻される値の型になります。
intなので、4byteの整数値になります。

sumは関数名になります。
次の(int max)ですが、int型のmaxという引数名になります。
つまり、4byteの整数値をmaxという名前の入れ物に入れて関数に渡されてきた。というイメージです。
引数名というのは、変数名と同じだと思っていただいて問題ないです。

関数に引数値を渡す

引数のmaxには、具体的な数値が呼び出し元で設定されます。
この数値のことを実引数と呼びます。

設計現場では、実引数と呼ぶ方はあまりいません。
単純に 引数 や、関数の引数 と呼ぶことが主です。

実際にどの様に使われて、処理が進んでいくのか見てみましょう。

#include <stdio.h>

int sum(int);  /* プロトタイプ宣言 */

int main(void)
{
    sum(50);   /* 50を渡ししている */
    return 0;
}

int sum(int max)
{
    printf("%d\n", (1 + max) * max / 2);

    return 0;
}

参考書と同じコードになります。
main関数の先頭からプログラムがスタートして、sum(50)に来ます。

50という数値をsumという名前の関数に渡しています。
sumの引数の型はint型でしたので、数値の範囲は符号付きの4byteとなります。
つまり、-2,147,483,647 から 2,147,483,647が設定可能な数値範囲です。

sum(50)に来たところで、11行目へ来ます。
この時点で max = 50となって遷移してきており、
13行目のprintf関数内に書かれているmaxは、sum(int max)のmaxと同じです。

故に、13行目のprint関数のmaxを50に置き換えて演算し、その結果が表示されるというのが処理になります。

int sum(int max)として、引数を渡す前提で関数が書かれているにもかかわらず、
呼び出し元で引数をセットしない場合エラーになります。

main関数の sum(50)をsum()と書いてしまうと、「引数がないよ!」とコンパイラが怒ってきますので、関数を引数必要として作成したのであれば、呼び出し元でも引数は渡すことです。

プロトタイプ宣言の引数の書き方

参考書では、int sum(int)と書かれています。
引数の部分は、型だけが書かれて引数名は記載されていません。
この書き方でもプログラム言語のルールとして問題ないのですが、
以下の書き方をする設計者の方が多いと思いますので、知っておいてください。

int sum(int max);  /* プロトタイプ宣言 */

プロトタイプ宣言でも引数名を書きます。
引数名を書いておくと、実際の関数処理部でも同じ引数名が存在しているので、
宣言と実処理が対になっていることが明らかで迷いがありません。

言語のルールとしては、引数名は書かなくても書いてもどちらも問題ないので、
プロトタイプ宣言の引数名は書きたくない方は、強制されるものではないのでそれでも構いません。

複数の引数設定

関数の引数は複数設定可能です。
上限はなく、メモリの制約や開発環境が処理できるのであれば、いくらでも設定できます。

但し、設計者やコード読者が分かりづらいので、最大で4つまでと規定されることが多いです。
ここは社内規約として決めごとすることが多いので、4つ以上でも構いませんが、
大量に引数を渡したい場合は、後段で出てくる構造体という仕組みを用いた方が良いので、
関数の引数としては、3つ4つ程度が良いと思います。

複数設定する書き方を見てましょう。

int sum(int min, int max)
{
    printf("%d\n", (min + max) * (max - min + 1) / 2);

    return 0;
}

引数は、型 + 引数名の組み合わせで記載します。
2つ目以降は、カンマ( , )で区切って書いていきます。
1つ目の引数を第一引数、2つ目の引数を第二引数と呼びます。

int sum(int min, int max)

例えば3つになった場合は以下です。
1つ目と2つ目の間はカンマ、2つ目と3つ目の間もカンマで区切ります。
引数の間はカンマで区切ると覚えてください。

int sum(int min, int max, int ave)

例えば、intで統一しなくても良いです。
第一引数は int型 の整数値、第二引数は char型の整数値、第三引数は unsigned short型の整数値です。
色んな方の数値を引数として渡すことができます。

int sum(int min, char max, unsigned short ave)

引数を2つ渡すパターンのコード例を見てみましょう(参考書と同じコードです)

#include <stdio.h>

int sum(int min, int max);  /* プロトタイプ宣言 */

int main(void)
{
    sum(50, 100);   /* 第一引数は50、第二引数は100を渡ししている */

    return 0;
}

int sum(int min, int max)
{
    printf("%d\n", (min + max) * (max - min + 1) / 2);

    return 0;
}

main関数内のsumに50,100を設定して渡しています。
引数の順番と設定する数値は対になるように意識してください。

ここでは、min = 50、max = 100となるように、
sum(50, 100)として、第一引数に50、第二引数に100を設定しています。
実際の関数の引数の位置に合わせて、設定したい値を書くようにします。

ちなみに、プロトタイプ宣言は参考書とは異なり、関数の引数名をそのまま書いてみました。

戻り値を設定してみる

これまで関数の解説では int sum(int max) などの様に、
先頭は int として、戻り値の型をintにしているものの、実際の関数の処理の最後は、
return 0; として 0 を返すような処理を書いてきました。

例えば、先ほどまで解説していた上記の処理では、main関数でsum関数を呼び出して、
sum関数の処理が終わると、main関数に戻るのですが、
その際には sum関数最後の return 0; の 0 が戻されてきます。
戻された 0 はどうするのか?戻されるってどういうこと?をこれから解説したいと思います。

#include <stdio.h>

int sum(int max);  /* プロトタイプ宣言 */

int main(void)
{
    int value;

    value = sum(50);   /* 50を渡ししている */

    return 0;
}

int sum(int max)
{
    int add; 

    add = max + 50;

    return add;
}

参考書からコードを変更してみました。
解説する上で複雑な計算をさせる必要もないので、極単純な計算で書いています。

まず、main関数内でsum関数から戻ってきた値を代入するための変数を用意します。
これが int value; です。
注意すべき点は、sum関数の処理終了時に戻される値の型は int型 なので、
変数も int型 にして型は揃えておかなければなりません。
ここが、char value などどしてしまうとエラーになります。

次に、value = sum(50); ですが、実際にsum関数に50を渡し、sum関数内の処理を経て終了時に戻された値をvalue変数に代入しています。

そのsum関数内の処理としては、引数 int maxに50が格納されてきます。
sum関数の先頭で、int add; として、main関数と同様に処理終了時に戻す値が int型 なので、
戻す値の型と同じ型で、addという名前の変数を用意します。

実処理は、maxに格納された50 + 50を加算して、addという変数に計算結果を代入しています。
つまり、add = 100という状態になったということです。

sum関数最後の return add; でaddの値をmain関数の呼び出し位置に戻されていきます。
つまり、add = 100なので100が、main関数の9行目の呼び出し位置に戻って、
valueという用意された変数に代入される。という流れです。

結果、9行目で value = 100 が格納されたということになります。
main関数の最後である11行目の return 0;が処理されて終了です。

余談ですが、戻り値は受け取らなくても構いません。
以下の様に、main関数でsum関数に50を渡して、sum関数内で50 + 50をした後、
演算結果である100をreturn addとして、main関数のsumを呼び出した位置に戻すのですが、
戻ってきた値を受け取る(=代入する変数)ものが用意されていなければ、そのまま捨てられるだけです。

引数は必ず渡さないといけませんが、戻り値は返されたものを受け取らなくても良いということです。

int main(void)
{
   sum(50);

   return 0;
}

int sum(int max)
{
    int add;

    add = max + 50;

    return add;
}

但し、捨てる前提なのであれば、戻り値を返す処理にする必要はありませんので、最初から何も返さない関数として処理を書くべき。となります。
つまり、以下の様な関数処理にして戻り値は無しとすべきです。

int main(void)
{
   sum(50);

   return 0;
}

void sum(int max)
{
    int add;

    add = max + 50;
}

void sum(int max)と書きましたが、先頭の intvoidに変わりました。
引数同様にvoidと記載すると何もなしという意味になります。
関数名の前にvoidを記載しますと、戻り値は設定なしとなり、関数内の処理を最後まで実行して終了します。

逆に、戻り値なしの設定で関数を作ったのに、main関数で以下の様に記載するとエラーになります。
valueで戻ってきた値を代入しようとしていますが、sum関数側はvoidで戻り値なし。とされていますので、main関数では戻り値がある前提、sum関数では戻り値なしの前提となり矛盾します。

int main(void)
{
   int value;

   value = sum(50);

   return 0;
}

void sum(int max)
{
    int add;

    add = max + 50;
}

戻り値は一つしか設定できません。
引数の様に複数設定はできませんので注意が必要です。

要点を整理しておきましょう。
 ・関数の引数は、複数設定可能だが4つ前後が理想である。
 ・関数の引数は、内部で処理を行う数値範囲に応じた型にすること。
 ・関数内部の処理に置いて戻り値の型は、関数宣言の型に合わせること。
  (int で書いたのに関数内のreturnにcharの変数を設定するなど、不一致はNG)
 ・呼び出し元で戻り値を受け取りたい場合は、戻り値の型に合わせて変数を用意すること。
 ・引数を必要とする関数を作成した場合は呼び出し元で引数の設定が必要(割愛できない)
 ・戻り値が返される関数として作成したが、呼び出し元で受け取らなくても問題ない。
 ・戻り値は一つだけしか設定できない