Cのコンパイラは、ソースコードを上から順に一度だけ読みます。まだ見たことのない関数がいきなり呼ばれると、「そんな名前は知りません」と怒って止まります。そこで先に名刺だけ渡しておくのがプロトタイプ宣言です。
#include <stdio.h>
int add(int a, int b); // ← プロトタイプ宣言(名刺)
int main(void)
{
printf("%d\n", add(2, 3));
return 0;
}
int add(int a, int b) // ← 定義(中身)
{
return a + b;
}プロトタイプ宣言は、関数の名前・戻り値の型・引数の型だけを伝えて、セミコロンで終わります。中身({ })は書きません。これがあるおかげで、main を先頭に置いて「まず全体の流れ、細部は後ろ」という読みやすい順番でファイルを書けます。
引数の名前は省略できます。int add(int, int); でも同じ意味です。ただし int max(int width, int height); のように名前を残しておくと、それ自体が説明になります。
宣言がいらない書き方
呼び出しより上に定義そのものを置けば、宣言は要りません。定義は宣言を兼ねるからです。
#include <stdio.h>
int add(int a, int b) // 先に定義してしまう
{
return a + b;
}
int main(void)
{
printf("%d\n", add(2, 3));
return 0;
}小さなプログラムならこれで十分です。ただしこの書き方だと、関数が増えるほど main がファイルのいちばん下へ押しやられ、全体の流れを追うのに最後までスクロールする羽目になります。また、2つの関数が互いを呼び合う場合は、どちらを上に置いても片方が「知らない関数」になるため、宣言なしでは解決できません。関数が5個を超えたあたりから、先頭にプロトタイプ宣言を並べる書き方のほうが快適になります。
実は毎回お世話になっている
printf が使えるのはなぜでしょう。答えは #include <stdio.h> です。あの1行は、printf などのプロトタイプ宣言がずらりと書かれたファイルを、その場に貼り付けています。つまり皆さんは1日目から、プロトタイプ宣言の恩恵を受けていたわけです。scanf も同じです。
罠・注意
- 末尾のセミコロンを忘れると、コンパイラは「中身のない定義」と解釈して、意味の分からないエラーを出します。
- 宣言と定義で型がずれていると(
int add(int, int);に対し定義がdouble add(double, double))、そこで初めてエラーになります。片方だけ直す修正は事故のもとです。 - 昔のCには「宣言がなければ int を返す関数とみなす」という暗黙のルールがありましたが、C99で廃止されました。C23基準では宣言なしの呼び出しはエラーです。
- 呼ぶ側より下に定義を置くなら宣言は必須。上に置くなら不要ですが、規模が大きくなると結局まとめて宣言したほうが読みやすくなります。
- 宣言だけ書いて定義を書き忘れると、コンパイルは通るのにリンクの段階で「実体が見つからない」というエラーになります。エラーの出る場所が違うので、慣れないうちは戸惑いがちなパターンです。
まとめ: プロトタイプ宣言=関数の名刺。戻り値の型 名前(引数の型); を呼び出しより上に置けば、定義はどこに書いてもかまいません。