今までは、C言語が用意してくれた printf や scanf_s といった関数を使って、プログラムを組み立ててきました。関数は自分で作る事も出来ます。今回はその関数を作る基本を学びます。
--
いきなりここに飛んで来ちゃった人は、よろしければ下記からご覧ください。

  • 関数の引数
関数は基本的に以下の形式となっています。

戻り値の型 関数名(引数1,引数2, ...) { 実行するプログラム return 戻り値。 }
関数名は自由な名前を付ける事が出来ます。但し、既にC言語やライブラリ(#includeで読み込んだファイルに定義済みの関数)が設定済みの名前は付ける事が出来ません。そのため、自分で printf という名前を付ける事は出来ません。
※ この定義済みの名前を予約語と言っています。

ただ、VS2022 では同じ関数名でも引数が違うと実装できてしまいます。これは関数のオーバーロードと言って C++ の機能です。C 言語では同名関数は記述できませんのでご注意ください。

関数名定義時に引数を指定する事が出来ます。この引数は型と名前がセットです。引数は何個でも指定することが出来ます。また、省略する事も出来ます。例えば、

void Func1(void)
このような記述なら引数はありません。

void Func2(int num)
こちらでは、呼び出しの際に引数が一つ必要となります。引数には呼び出し時に指定された値が引き渡されてきます。例えば呼び出し側が Func2(10); と記述した場合は、num は 10 という初期値が格納されています。

もう少し具体的に記述してみます。

void PrintScore(int scr) { printf(LOCATE, 1, 1); printf("SCORE:%08d", scr); } void main() { int score = 100; PrintScore(score); }
main からプログラムが始まります。ローカル変数 score が 100 という値で初期化されます。その変数が自分で作成した PrintScore という関数の引数として指定されています。PrintScore 関数側ではその値 100 が格納された scr という変数が使用可能となります。その値を使って、画面上部に数値を8桁で表示するというワケです。

関数のスコープが終了すると、プログラムは呼び出し元に戻って引き続き処理が継続されます。
※ 最近コレに買い換えました。安くて使いやすくてとても良いと思います。なるべく安くと思っているのであればオススメします。

  • 関数の戻り値
関数の終了時に呼び出し先に何らかの値を返却できます。その指定を行うのが return です。戻り値に void を指定した場合は、値の指定は出来ません。void は値無しという意味なのです。この return で返す値を返り値または返却値と呼びます。

関数のスコープ {} が終わると自動的な返り値無しで関数の実行が終了します。関数の実行途中で return が実行されると、いきなりその関数での動作は終了します。

また具体例を示してみます。

int GetAnswer(void) { while (true) { rewind(stdin); char key = getchar(); if (key == 'Y' || key == 'y') return 1; if (key == 'N' || key == 'n') return 0; if (key == '\n') break; Sleep(1); } return -1; } void main() { char msgs[][4] = { "No", "Yes", }; printf(LOCATE, 1, 1); printf("カラーに設定しますか?(y/n) "); int ans = GetAnswer(); if (ans < 0) return; printf(LOCATE, 1, 29); if (ans > 0) printf(FYELLOW); printf(msgs[ans]); }
いろいろ新しい事を盛り込んでみました。まず関数として GetAnswer を作りました。引数は無し、返却値は int として Y キーが押されたら 1 を、N キーが押されたら 0 を、何も入力されていなければ -1 を返却するようにしています。それ以外のキーの場合は、有効な入力がくるまで待機しています。

getchar は1文字だけキーボード入力待ちをする C言語の関数です。Enter キーだけ押されると、改行を示す '\n' が返ってきます。ここで return とせず break; として、無限ループを抜けているのは、関数の終了箇所に return -1; を記述したかったためです。返却値が必要な関数で、スコープの最後に return がないのは、将来のバグを引き起こす可能性があるので、それは避ける記述としています。

GetAnswer() の返却値を変数 ans に格納して、その値が -1 なら return としています。現在の main は返却値が不要な void なので、そのまま return としています。実は今まで使っていた void main() は省略された型であり、現在は使用が推奨されていません。
※ この後すぐに正しい main 関数の形式を説明します。

変数 ans は 0 または 1 が格納されているのが保証されましたので、その値を使って配列に格納されている文字列を表示しています。
※ 使っているキーボードがこちら。タイミングの音が静かで響かないので気に入っています。キーボードなんて打てれば良いと思ってるならオススメ。仕事柄かなりキーボードは酷使していますが、問題なく使えてますです。

  • 本来の main 関数
今まで、ずっと VS2022 のエラー一覧に C4326 警告が表示され続けていました。これは最初の段階では関数の説明をしていなかったため、昔は許されていた古い形式の main を使っていたため警告され続けていたのです。main 関数に用意されている形式は…

int main(void) int main (int argc, char *argv[])
実はこの2種類だけです。void main() は昔の資産がコンパイルできるように残されているだけですので、新しく記述する場合は使用してはいけないのです。(void) を () と省略できるのは C++ や C# では void 扱いですが、C言語では微妙に意味合いが異なります。そのため、C言語では引数の省略では void を記述するようにします。
※ void の省略に関してはコンパイラ依存の可能性もあります。

2番目の main に書き方を説明するためには、ポイントの理解が必要なので、こちらはもう少し後で解説します。今回重要なのは、今後は void main() は使わず、int main(void) を使用するという事ですね。

さて、そうなると先ほどのプログラムで困る事になります。

int main(void) { char msgs[][4] = { "No", "Yes", }; printf(LOCATE, 1, 1); printf("カラーに設定しますか?(y/n) "); int ans = GetAnswer(); if (ans < 0) return; printf(LOCATE, 1, 29); if (ans > 0) printf(FYELLOW); printf(msgs[ans]); }
ans が -1 の時の return に返却値がありません。また、Yes, No の表示で main 関数が終了するのですが、やはり返却値の指定がありません。この状態だとコンパイルエラーになり実行できなくなります。

では、main 関数の返却値は何を戻せば良いのでしょうか。実はどんな数値でも良かったりします。外部からプログラムを呼び出されたときの返却値として使用されるのですが、コマンドプロンプトツールとして作成しない限りは、細かく返却値は設定する必要はないのです。

私はこのような場合は以下の定数値を好んで使っています。

EXIT_SUCCESS 処理が正常終了した EXIT_FAILURE 処理が失敗した
この定数値は stdlib.h 内で define 定義されています。この値を return に使います。先のプログラムでは…

int main(void) { char msgs[][4] = { "No", "Yes", }; printf(LOCATE, 1, 1); printf("カラーに設定しますか?(y/n) "); int ans = GetAnswer(); if (ans < 0) return EXIT_FAILURE; printf(LOCATE, 1, 29); if (ans > 0) printf(FYELLOW); printf(msgs[ans]); return EXIT_SUCCESS; }
こんな感じでしょうか。入力が中断された時は EXIT_FAILURE、それ以外は EXIT_SUCCESS を返却しています。具体的な数値は EXIT_SUCCESS が 0 で、EXIT_FAILURE が 1 となっています。今後の main 関数は、戻り値として int を指定するようにしてください。
※ これでようやく警告が全て消えます…


※ 液晶ペンタブも安くなりましたよね。今は十分手が届く価格になってます。