朝の支度を人に説明するとき、「歯ブラシを取って、水を出して、みがいて、口をすすいで……」と毎回すべてを言う人はいません。ふつうは「歯をみがく」の一言で済ませます。手順のかたまりに名前を付けたからです。
プログラムでも同じことをします。処理のかたまりに名前を付けて、いつでも呼び出せる部品にしたもの。それが関数です。ここまで毎回書いてきた main も、実は関数のひとつでした。
基本の形
戻り値の型 関数名(引数リスト)
{
処理;
}いちばん簡単なのは、値を受け取らず、値も返さない関数です。
#include <stdio.h>
// 区切り線を表示するだけの関数
void print_line(void)
{
printf("--------------------\n");
}
int main(void)
{
print_line();
printf(" 本日の売上\n");
print_line();
return 0;
}実行すると、区切り線・見出し・区切り線の3行が表示されます。print_line(); と書いた場所でいったん関数の中身に飛び、終わったら元の場所に戻ってくる。これが呼び出しです。
実行の順番に注意
「ソースコードは上から下へ実行される」という思い込みは、ここで一度捨ててください。print_line の中身はファイルの上のほうに書かれていますが、それが動くのは main の中で呼ばれた瞬間です。定義は「置いてあるだけ」で、呼ばれるまで一度も実行されません。プログラムの実行はいつも main から始まり、呼び出しのたびに行ったり来たりします。
なぜ部品にするのか
理由は3つあります。ひとつめ、同じ処理を何度も書かずに済むこと。ふたつめ、print_line という名前そのものが説明になり、main を上から読むだけで流れが分かること。みっつめ、直すときに1か所だけ直せばよいこと。区切り線を - から = に変えたくなっても、関数の中の1行を書き換えるだけで、呼び出しているすべての場所に反映されます。
名前の付け方にもコツがあります。関数は「何かをする」ものなので、print_line count_words のように動詞から始めると、読んだ瞬間に役割が伝わります。
罠・注意
void が2か所に出てきますが、意味は別々です。前の void は「返す値はない」、カッコの中の void は「受け取る値はない」。C23では print_line() と空カッコにしても「引数なし」の意味になりますが、意図がはっきりする (void) を書いておくのが無難です。
- 関数の中に関数は書けません。Cでは関数の定義はすべて横並びです(入れ子は禁止)。
- 定義の閉じ
}の後ろにセミコロンは要りません。};は構造体と混同しやすい癖です。 - 呼ぶときの
()を忘れると実行されません。print_line;は「その関数を指し示すだけ」で、何も起きないまま素通りします。 - 関数名は変数名と同じ規則(英字・数字・アンダースコア、先頭は数字不可)です。既にある標準関数と同じ名前を付けると衝突するので、
printfのような名前は避けます。
まとめ: 関数は「名前の付いた処理のかたまり」。作って、呼ぶ。たったこれだけで、長いプログラムが読める大きさに分かれていきます。