前回は、プロジェクト「0210_myfunc」を作り、関数を自作する方法についてお話しました。
今回は、新しくプロジェクト「0220_func_prototype」を作り、関数のプロトタイプ宣言についてお話します。関数が大量になったときなど、関数のプロトタイプ宣言をすることの利点を、お話していきます。
自作の関数を作りとき、関数のプロトタイプ宣言を行った時のソースコードを書いて動かしてみましょう。プログラムの結果は前回と同じとなるはずです。
プロジェクト「0220_func_prototype」を新しく作成し、ソースコード「MyFuncPrototype.c」を作成してみてください。
#include <stdio.h>
#include <stdlib.h>
// 関数のプロトタイプ宣言
int kinokoHiroi();
int main() {
for (int i = 0; i < 4; i++) {
int toretaKinoko = kinokoHiroi();
printf("ぷらんくは %d 本のキノコを採取しました。\n", toretaKinoko);
}
}
int kinokoHiroi() {
int kinoko = rand() % 3 + 1;
return kinoko;
}
このコードを実行してみるとこんな結果になったかと思います。
ぷらんくは 3 本のキノコを採取しました。
ぷらんくは 3 本のキノコを採取しました。
ぷらんくは 2 本のキノコを採取しました。
ぷらんくは 2 本のキノコを採取しました。
同じように、ぷらんくはランダムに4回、キノコを採取しました。
プロトタイプ宣言とはプロトタイプ(prototype)とは日本語で「原型」」とか「建築でいうところの最初の模型」といった意味があります。
ソースコードの最初のほうで、あらかじめこんな名前・形式の関数を使いますよ、と宣言しておくことをプロトタイプ宣言と呼びます。 プロトタイプ宣言はどんなことに役に立っているでしょうか。
前回のソースコードでも関数を作りましたが、そこではプロトタイプ宣言は出てきていませんでした。これはビルドするときの流れに関係があります。
Visual Studio 等でコンパイルという処理をすると、ソースコードからCPUが読み込みできる機械語への変換が行われます。 その時、C言語のソースコードを上から下に向けて読み込んでいきます。順番に上から下にソースコードを読むとき、関数の宣言と定義を読み込んでいきますが、 関数の中身が定義を読み込む前に、関数の呼び出しが行われてしまうと、「そんな関数みつからんし・・。上から見ていったけど宣言・定義も書いてなかったし・・。」ということでエラーとなってしまいます。
試しに、今回のコードで 4 行目のプロトタイプ宣言をコメントアウトすると、上のような警告メッセージが表示されます。
Visual Studio では一応、main関数の後で書いてある kinokoHiroi 関数を呼びだせばいいのでは・・? と紐づけして、実行できる状態にはしてくれます。 つまり、自作関数を呼び出すような処理の前に、関数の宣言と定義が書いてあれば問題ありません。
ですが、これも関数が多くなってくると問題があります。 関数から関数を呼び出すこともありますし、どの関数を最初に宣言、定義してあげればよいのか複雑になってきます。
例えば、funcA の中から funcC を呼びだしているとき、funcC を先に書かないといけないですね。funcC からさらに funcB を呼び出しているときはどうでしょうか。
funcB → funcC → funcA → main 関数の順番で書いていかないといけません。実際に大きなプログラムを書くときは、関数の呼び出しの順番はさらに複雑になることもあります。
プロトタイプ宣言をすることで、このようなスースコードの書く順番を考えて関数の宣言&定義をしなくても良くなります。
プロトタイプ宣言を、ソースコードの最初に書いてあげることで、 「このソースコードの中ではとりあえず、こういう名前と引数を持つ関数が後ろの方で具体的に書いてある(定義してある)予定なので、中身までは書いていないけど呼び出しできるハズ」とコンパイラに教えてあげることができます。
プロトタイプ宣言の書き方は下のようになります。
戻り値の型 関数名 ( 引数1の型 引数1の変数名, 引数2の型 引数2の変数名, … 略 … );
引数の変数名は省略して、下のように書くこともできます。どちらでも問題なく同じ動きをしますが、なんとなく直感的にどのような値を入れたらよいのかわかるように、変数名は書いておくことをお勧めします。
※VisualStudio などの IDE による関数名の参照機能(IntelliSense)などで、どのような関数なのか情報を表示するときに役に立つことがあるからです。
戻り値の型 関数名 ( 引数1の型, 引数2の型, … 略 … );
前回書いた関数の宣言&定義のうち、{…}を省略した形になっており、 これで、こういった関数が用意されていると宣言できます。
今回のソースコードを簡単に上から見てみましょう。
キノコ拾い関数のプロトタイプ宣言
// 関数のプロトタイプ宣言
int kinokoHiroi();
ソースコードの include 文の直後に、関数のプロトタイプ宣言が行われています。
「int kinokoHiroi();」という形式の関数がこのソースコードの中のどこかで定義されていることを示しており、「今後 kinokoHiroi() という関数の処理(定義)がまだ読み込まれていないときでも、ちゃんと kinokoHiroi() という処理はどっかに書いてあるので、エラーにしないでね!」ということをコンパイルに伝えてあげています。
その後6行目から main 関数の定義に入ります。
int main() {
for (int i = 0; i < 4; i++) {
int toretaKinoko = kinokoHiroi();
printf("ぷらんくは %d 本のキノコを採取しました。\n", toretaKinoko);
}
}
main関数の中で 8 行目に kinokoHiroi() 関数を呼び出しするように書いてありますが、もしプロトタイプ宣言が書いていなければ、「そんな関数ないです」というエラーや警告メッセージが出て、正しくビルドが失敗する可能性があります。
13行目からプロトタイプ宣言していた kinokoHiroi 関数の定義をしています。
int kinokoHiroi() {
int kinoko = rand() % 3 + 1;
return kinoko;
}
これを書かないと、main関数から kinokoHiroi 関数を呼び出ししようとしたとき、「やっぱそんな関数ないじゃん」ということでエラーになってしまいます。
プロトタイプ宣言の基本についてお話しました。今後ソースコードのサイズが大きくなっていき、たくさんの関数を扱うようになったときは、地味ながら必須の知識になるかと思います。
今回はここまで。おつかれさまでした。
更新日 | 更新内容 |
---|---|
2018/9/2 | 初版公開 |
2018/11/24 | アイキャッチ画像、トップ画像更新 |
コメント、ありがとうございます。
ごめんなさい。エラーでうまく送信できませんでした。ご迷惑をおかけします。しばらくおいてから再度送信を試していただくか、以下から DM などでご連絡頂ければと思います。
Twitter:@NodachiSoft_jpお名前:以下の内容でコメントを送信します。よろしければ、「送信」を押してください。修正する場合は「戻る」を押してください
お名前: