
C言語基礎|関数原型宣言と関数定義
コンパイラにも“事前に自己紹介”が必要
C言語のコンパイラは、私たちが本を読むみたいに 上から下へ 順番にコードを読んでいきます。
だから、main の途中でいきなり未知の関数が出てくると、
「え、これ何? 引数いくつ? 戻り値の型は?」
ってなって困ります。
その“困らないための自己紹介”が 関数原型宣言(プロトタイプ宣言) です。
そして、実際に処理の中身(本体)を書いて関数を完成させるのが 関数定義。
この2つの役割をきちんと分けて理解すると、プログラムが一気に読みやすく、直しやすくなります。

サンプルプログラム
2つの整数を読んで、差の絶対値を表示するプログラムを例に解説をします。
プロジェクト名:chap6-12-1 ソースファイル名:chap6-12-1.c
Visual Studio でこのプログラムを実行するには、SDLチェック設定を変更しておく必要があります。
1.プロジェクト名を右クリックして、「プロパティ」をクリックします。
2.「C/C++」→「全般」→「SDLチェック」を「いいえ」に切り替えて「OK」をクリックします。
#include <stdio.h>
// 関数原型宣言(プロトタイプ)
int abs_diff(int x, int y);
int main(void)
{
int a, b;
puts("2つの整数を入力してください。");
printf("1つ目:");
scanf("%d", &a);
printf("2つ目:");
scanf("%d", &b);
printf("差の大きさは%dです。\n", abs_diff(a, b));
return 0;
}
// 関数定義(中身)
int abs_diff(int x, int y)
{
if (x > y)
return x - y;
else
return y - x;
}実行例
2つの整数を入力してください。
1つ目:20
2つ目:55
差の大きさは35です。このプログラムでは、main が abs_diff を呼び出す前に、上で関数原型宣言があるので、コンパイラは安心して読み進められます。
まず整理:宣言と定義は何が違うの?
関数原型宣言と関数定義の違い
| 項目 | 何を書く? | 目的 | 末尾のセミコロン | 実体(中身)は作る? |
|---|---|---|---|---|
| 関数原型宣言 | 返却値型 / 関数名 / 仮引数の型 | 仕様を先に伝える(呼び出し可能にする) | 必要 | 作らない |
| 関数定義 | 原型宣言の内容 + { } の中身 | 実際の処理を書く(関数を完成させる) | 不要 | 作る |
この表は「原型宣言=仕様だけ」「定義=仕様+中身」を一発で見分けられるようにするための整理図です。
コンパイラが読む順番と“事前情報”
上から読むコンパイラとプロトタイプ
| コードの並び | コンパイラの気持ち |
|---|---|
| int abs_diff(int x, int y); | OK、あとでこの関数が出てくるんだな(型も把握) |
| main の中で abs_diff(a, b) | OK、呼び出し方合ってるかチェックできる |
| int abs_diff(int x, int y) { ... } | OK、中身ここね |
この図のポイントは「呼び出し位置より前に、最低限の仕様が必要」ということです。
関数原型宣言の書式と役割
書式
- 返却値型 関数名(仮引数型並び);
例:
- int abs_diff(int x, int y);
何をする“命令”なの?
関数原型宣言は、次の情報を コンパイラ と 人間(読み手) に伝えます。
- 返却値は int
- 引数は int を2つ受け取る
- 関数名は abs_diff
これがあるおかげで、呼び出し側で 間違った呼び方 をすると検出しやすくなります。
関数定義の書式と役割
書式
- 返却値型 関数名(仮引数宣言) { 文… }
例:
- int abs_diff(int x, int y) { ... }
何をする“命令”なの?
関数定義は、関数という部品を完成させるものです。
原型宣言が「仕様書」なら、定義は「完成品+中身」です。
どこに置くのが読みやすい?おすすめ配置パターン
関数の配置には大きく2つの流派があります。
配置パターンの比較
| パターン | 並び | 関数原型宣言 | 長所 | 短所 |
|---|---|---|---|---|
| A:定義を先に置く | 使われる関数 → main | なくても動く | 原型宣言が不要でシンプル | main が下に沈みやすい |
| B:原型→main→定義 | 原型宣言 → main → 関数定義 | 必要 | main を先に読めて全体像が早い | 仕様変更時に原型と定義の両方修正が必要 |
教材やチーム開発では、main を早く見せたいことが多いので B がよく使われます。
一方で、小さめのプログラムでは A もスッキリしていてアリです。
“仕様変更”の注意点:原型と定義がズレると危ない
たとえば abs_diff を
- long を返すようにした
- 引数を3つに増やした
みたいに変更したとき、原型宣言と関数定義の両方を直さないと、混乱の元になります。
ありがちなトラブル例(イメージ)
- 原型:int abs_diff(int, int);
- 定義:int abs_diff(int, int, int) { ... }
こういうズレがあると、コンパイルエラーや警告の原因になります。
なので「原型と定義は必ず同じ仕様にする」って覚えておくと安全です。
重要ポイント(短くまとめ)
- コンパイラは上から読むので、呼び出しより前に関数の仕様が必要
- 関数原型宣言は仕様を伝えるだけ(中身は作らない)
- 関数定義は中身を作って関数を完成させる。
- 置き方は2パターンあるが、読みやすさ重視なら 原型→main→定義 が便利なことが多い。
