苦しんで覚えるC言語 11.1 自作関数を作る P198 – P203

プログラムの部品化

これまでmain関数しか解説をしていませんが、膨大なプログラムを全てmain関数内に記述するわけにもいかず、機能や処理毎に可読しやすい記述をしていきたいものです。

その上で、処理単位毎に纏まりのある記述の仕方として、関数化してプログラムする方法があります。
そこで自作関数の方法を解説していきたいと思いますので、参考書の例を用いて解説したいと思います。

#include <stdio.h>

int main(void)
{
   return 0;
}

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

    return 0;
}

main関数はこれまでと同じくですが、処理が return 0; となっております。
自作関数として int sum(void) を新たにつくりました。

関数のルールはmain関数の解説の時と同じです。
int sum(void) のintは関数内の処理の最後に呼び出し元に返されるデータの型を意味します。
この関数では、 return 0; の 0 が整数値なので整数値が返されることを示しています。

int sum(void) のsumは、関数名です。
int sum(void) のvoidは、関数に渡される引数が何もないことを明示しています。

関数の処理は { }の中に書かれたコードとなり、以下の2行が該当します。
printf(“%d\n”, (1 + 100) * 100 / 2);
return 0;

詳細は上述の通りですが、このままでは機能していません。
プログラムはmain関数から実行されますので、上述のコードは、main関数内のreturn 0;が実行されて終了してしまいます。

その為、sum関数は用意できたが、どこからも使われていない状態になります。

プロトタイプ宣言

どこからも使用されていないことに加えて、もう一つ準備が必要です。
ファイル上はsum関数が書かれていますが、コンパイラはその関数を認識できていません。

コンパイラに対して、sum関数が存在し、使われることを教えなければならず、
その方法が2つあります。

[方法1] main関数よりも上にsum関数を記載する

#include <stdio.h>

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

    return 0;
}

int main(void)
{
   return 0;
}

ここではmain関数とsum関数しかないので、main関数よりも・・・と解説していますが、正しい表現としては、sum関数が呼び出される関数よりも上に書く。ということです。

main関数以外にも複数の自作関数を用意するようなプログラムになった場合、
main関数より上に書けば良い。と覚えてしまうと失敗します。

他の自作関数から呼び出される使い方となる為、呼び出し元になる関数よりも上の方に関数処理を書いておく必要があります。
単純な言い方をすれば、使われる関数よりも先に関数処理を書いておかないとコンパイラがどこで使われているか判断できない。といった感じです。

ちなみに、このプログラムもmain関数から処理開始され、main関数内のreturn 0; で終了となりますので、sum関数はどこからも呼び出しされていません。
ただ単にmain関数よりも上にsum関数を記載しただけ。のものです。

[方法2] プロトタイプ宣言を書く

#include <stdio.h>

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

int main(void)
{
   return 0;
}

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

    return 0;
}

自作関数をどの関数より先に書くべきか?を意識するのも大変なので、
ファイルの先頭に、int sum(void); 関数の1行目の部分を書き、
このファイルの中で int sum(void) という関数が用意されて使用されていますよ。を明示します。

これで、コンパイラはファイル内で記載された関数があることを理解してくれるようになります。
これをプロトタイプ宣言と呼びます。

尚、プロトタイプ宣言を書いた場合、自作関数の各順番は気にする必要がありません。
main関数よりも上にsum関数を書いても良いですし、上述の様にmain関数の下にsum関数を書いても良いです。

自作関数を呼び出す

ここまでに書いた上述の参考プログラムは、main関数のreturn 0; で処理終了となるため、自作関数は全く機能していません。

自作関数が機能するには、どの様に書けばよいかを解説したいと思います。

#include <stdio.h>

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

int main(void)
{
   sum();  /* 呼び出し部分 */
   return 0;
}

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

    return 0;
}

プログラムはmain関数から開始されますので、sum();が呼び出されます。
関数を使う際は、int sum(void) をそのまま書くのではなく、
以下の様に関数名と引数だけを記載します。

 sum()

引数部分は、void なので、冒頭の解説の通り関数に渡される引数が何もないので、
( ) の中には何も記載せずにカッコだけ記載します。
戻り値の型である int は呼び出し部分では記載しません。

上記では7行目の sum(); に処理が来ると13行目のsum関数内の処理へ遷移します。
13行目の処理を行って計算結果を表示した後、15行目の return 0;に遷移しまして、
main関数に 0 を持って戻ります。
つまり、8行目に戻ってきます。

main関数の8行目もreturn 0; なので、ここでmain関数も処理終了となり、全体のプログラム処理が終了になります。

この様な要領で、sum関数のみならず自作関数を2個、3個・・・100個と、
必要に応じてどんどん増やすことになります。
当然、どんどん増やすと言っても、処理単位に相応しい関数とすることが理想なので、
むやみに自作関数化するのも違うという話にはなります。

自作関数化する基準

これは設計者の設計思想に依存します。
一般的には、関係する機能毎に関数化することが多いです。

例えば、初期化処理。
初期化処理は、色んな機能を使用する場合は機能毎に自作関数にして初期化することをお勧めします。

A/D変換機能、シリアル通信機能、電源に関わる設定処理・・・etc
使用している機能を一つの関数に詰め込んで書いても問題はないのですが、
機能毎に初期化処理を関数にして書くことで、パッと見た際にわかりやすいですし、
変更・修正の際も書き換えする範囲が限定されます。