C++入門|プロトタイプ宣言と関数

関数は、何度も使う処理に名前を付けた必殺技。プロトタイプ宣言で先に技の存在を知らせておけば、main関数から安心して呼び出せます。

C言語の基本文法を学んでいくと、だんだんプログラムの中に同じような処理が何度も出てくるようになります。

たとえば、2つの数値を使って平均を求める処理があったとします。1回だけなら、その場で計算式を書けば大丈夫です。
しかし、同じような平均計算を何度も行う場合、毎回同じ計算式を書くのは少し面倒です。

そこで登場するのが 関数 です。

関数とは、ひとまとまりの処理に名前を付けて、必要なときに呼び出せるようにしたものです。

ドラゴンボール風にたとえるなら、関数は名前を付けた必殺技のようなものです。
毎回、気を集める手順、構える手順、撃つ手順を細かく書くのではなく、気合砲のような技名を決めておけば、必要なときにその技を呼び出せます。

C言語でも同じです。
何度も使う処理を関数としてまとめておけば、プログラムを短く、読みやすく、直しやすくできます。

今回のテーマは、関数プロトタイプ宣言 です。

関数は、処理を部品としてまとめるための大切な仕組みです。
プロトタイプ宣言は、その関数があとで登場することを、あらかじめコンパイラに知らせるための宣言です。

学ぶ内容意味ドラゴンボール風のイメージ
関数名前を付けた処理のまとまり名前を付けた必殺技
引数関数に渡す値技に込める気の量
戻り値関数から返ってくる結果技を使ったあとの測定結果
return文結果を返して関数を終える修行結果を報告して戻る
プロトタイプ宣言関数の存在を先に知らせる技の目録を先に見せる

関数を理解すると、C言語のプログラムは一気に整理しやすくなります。

同じ処理を何度も書くのではなく、処理に名前を付けて呼び出す。
この考え方は、今後C++でさらに大きなプログラムを作るときにも、とても大切な土台になります。

関数を使ったプログラム

まずは、関数を使ったサンプルプログラムを見てみましょう。

ここでは、2つの気の値を受け取り、その平均を求める関数を作ります。
平均を求める処理を関数にしておくことで、いろいろな値に対して同じ計算を簡単に使い回せます。

プロジェクト/ファイル名: Chap1_07/main.c

#include <stdio.h>

// プロトタイプ宣言
double calc_ki_balance(double, double);

int main(int argc, char** argv) {
    double result1, result2, result3;
    double base_ki = 2.4, training_ki = 4.8, support_ki = 3.6;

    // 関数を呼び出して気の平均値を求める
    result1 = calc_ki_balance(base_ki, training_ki);
    result2 = calc_ki_balance(5.5, 7.5);
    result3 = calc_ki_balance(support_ki, 6.0);

    // 計算結果を表示する
    printf("result1 = %f, result2 = %f, result3 = %f\n", result1, result2, result3);

    // プログラムが正常に終わったことを示す
    return 0;
}

// 気の平均値を求める関数の定義
double calc_ki_balance(double first, double second) {
    // 2つの気の値の平均を求める
    double balance = (first + second) / 2.0;

    // 計算した平均値を呼び出し元へ返す
    return balance;
}

このプログラムを実行すると、次のように表示されます。

result1 = 3.600000, result2 = 6.500000, result3 = 4.800000

このプログラムでは、calc_ki_balanceという関数を作っています。

calc_ki_balanceは、2つのdouble型の値を受け取り、その平均を計算して返す関数です。

呼び出し渡す値戻ってくる値
calc_ki_balance(base_ki, training_ki)2.4 と 4.83.6
calc_ki_balance(5.5, 7.5)5.5 と 7.56.5
calc_ki_balance(support_ki, 6.0)3.6 と 6.04.8

同じ平均計算を3回行っていますが、計算式を3回書いているわけではありません。
calc_ki_balanceという関数を呼び出すことで、同じ処理を何度も使っています。

ドラゴンボール風にたとえるなら、気の安定値を測る専用の修行技を作り、その技を何度も呼び出しているイメージです。

図:関数は名前を付けた処理のまとまり

この図が示していること

この図では、関数が入力を受け取り、処理を行い、結果を返す仕組みを表しています。

左から入ってくる2.4と4.8が引数です。
中央のcalc_ki_balance関数が、その2つの値を使って平均を計算します。
そして、右側に3.6という戻り値を返します。

関数は、何度も使う処理をひとまとめにして、名前を付けて呼び出せるようにする仕組みです。
修行装置に値を入れると結果が出てくる、と考えると理解しやすくなります。

関数とは何か

関数とは、処理に名前を付けてまとめたものです。

C言語では、main関数も関数のひとつです。
printfも、C言語に用意されている関数です。
そして、今回のcalc_ki_balanceのように、自分で関数を作ることもできます。

関数を使うと、同じ処理を何度も書かずに済みます。

たとえば、2つの値の平均を求める式は、次のように書けます。

(first + second) / 2.0

この計算を何度も使うなら、そのたびに式を書くよりも、calc_ki_balanceという名前を付けた関数にしておくと便利です。

関数の役割内容
処理をまとめる同じような処理を1つに整理できる
名前で呼び出せる関数名を書くことで処理を実行できる
値を受け取れる引数を使って外から値を渡せる
結果を返せる戻り値として計算結果を返せる

ドラゴンボール風にたとえるなら、関数は技の名前です。

毎回、手順を細かく書かなくても、技名を呼べば、その技にまとめられた処理が実行されます。
この仕組みがあることで、プログラムを短く、読みやすく、再利用しやすくできます。

関数の基本書式

関数の基本的な書き方は、次のようになります。

戻り値の型 関数名(引数の型 引数1, 引数の型 引数2) {
    処理
    return 戻り値;
}

今回のプログラムでは、次の部分が関数の定義です。

double calc_ki_balance(double first, double second) {
    double balance = (first + second) / 2.0;
    return balance;
}

この関数を分解すると、次のようになります。

部分意味
double戻り値の型。小数を返す
calc_ki_balance関数名
double first1つ目の引数。小数を受け取る
double second2つ目の引数。小数を受け取る
{ }関数の処理範囲
double balance = ...平均値を計算して変数に入れる
return balance;計算結果を呼び出し元へ返す

ドラゴンボール風にたとえるなら、関数定義は技の説明書です。

どんな種類の結果を返すのか、技の名前は何か、どんな値を受け取るのか、技の中で何をするのかが書かれています。

引数は関数へ渡す値

関数に渡す値を 引数 といいます。

今回の関数では、firstとsecondの2つが引数です。

double calc_ki_balance(double first, double second)

この関数を呼び出すときは、次のように2つの値を渡します。

result1 = calc_ki_balance(base_ki, training_ki);

このとき、base_kiの値がfirstに入り、training_kiの値がsecondに入ります。

呼び出し元の値関数側の引数
base_kifirst
training_kisecond

base_kiには2.4、training_kiには4.8が入っているため、関数の中では次のような状態になります。

関数内の変数
first2.4
second4.8

ドラゴンボール風にたとえるなら、引数は技に込める気の値です。

calc_ki_balanceという技に、2つの気の値を渡すことで、その平均値を計算できます。
関数は、渡された値を使って処理を行います。

戻り値は関数から返ってくる結果

関数が処理を行ったあと、呼び出し元に返す結果を 戻り値 といいます。

今回の関数では、平均値を計算してbalanceに入れ、そのbalanceをreturnで返しています。

double balance = (first + second) / 2.0;
return balance;

たとえば、firstが2.4、secondが4.8なら、次の計算が行われます。

(2.4 + 4.8) / 2.0 = 3.6

その結果、balanceには3.6が入ります。
そして、return balance; によって、3.6が呼び出し元へ返されます。

呼び出し元では、次のように戻り値をresult1に代入しています。

result1 = calc_ki_balance(base_ki, training_ki);

つまり、この行は次のような意味になります。

処理内容
calc_ki_balanceを呼び出す2つの値の平均を計算する
戻り値が返る3.6が返る
result1に代入するresult1に3.6が入る

ドラゴンボール風にたとえるなら、戻り値は技を使ったあとの測定結果です。

修行装置に2つの気の値を入れると、平均値という結果が返ってきます。
その結果をresult1という記録欄に保存しているわけです。

図:引数と戻り値の流れ

この図が示していること

この図では、関数に引数を渡し、関数の中で処理を行い、戻り値を返す流れを表しています。

base_kiとtraining_kiの値が、calc_ki_balance関数へ渡されます。
関数内では、それぞれfirstとsecondとして受け取られます。
そのあと、平均値を計算してbalanceに入れ、returnで3.6を返します。

戻ってきた3.6は、呼び出し元のresult1に代入されます。
この流れが、関数呼び出しの基本です。

return文は結果を返して関数を終える

return文は、関数の処理を終えて、呼び出し元に値を返すための文です。

今回の関数では、次のように使っています。

return balance;

これは、balanceの値を呼び出し元へ返すという意味です。

関数の戻り値の型がdoubleなので、returnで返す値もdouble型の値になります。
今回の場合は、平均値が小数になる可能性があるため、double型を使っています。

書き方意味
return balance;balanceの値を返して関数を終える
return 0;main関数で正常終了を知らせる
return 戻り値;関数の結果を呼び出し元へ返す

main関数のreturn 0;も、同じreturn文です。
ただし、main関数の場合は、プログラムが正常に終了したことをOSへ知らせる役割があります。

ドラゴンボール風にたとえるなら、returnは修行結果を報告して元の場所へ戻る合図です。
関数の中で計算した結果を持って、呼び出した場所へ帰っていきます。

プロトタイプ宣言とは何か

今回のプログラムでは、main関数よりあとにcalc_ki_balance関数の定義があります。

int main(int argc, char** argv) {
    ...
}

double calc_ki_balance(double first, double second) {
    ...
}

C言語では、関数を呼び出す前に、その関数がどのような形なのかをコンパイラに知らせておく必要があります。

そこで使うのが プロトタイプ宣言 です。

今回のプログラムでは、次の行がプロトタイプ宣言です。

double calc_ki_balance(double, double);

これは、calc_ki_balanceという関数があとで登場し、double型の値を2つ受け取り、double型の値を返すことを先に知らせています。

部分意味
double戻り値の型
calc_ki_balance関数名
double, double引数としてdouble型を2つ受け取る
;宣言の終わり

ドラゴンボール風にたとえるなら、プロトタイプ宣言は技の目録です。

実際の技の詳しい中身はあとで説明します。
でも、先に「calc_ki_balanceという技があり、2つの気の値を受け取り、気の平均値を返す技です」と修行装置に知らせておくわけです。

これにより、main関数の中でcalc_ki_balanceを呼び出しても、コンパイラは「この関数はあとで定義されるのだな」と理解できます。

プロトタイプ宣言と関数定義の違い

プロトタイプ宣言と関数定義は似ていますが、役割が違います。

プロトタイプ宣言は、関数の形だけを先に知らせるものです。
関数定義は、実際に何をするのかを書いたものです。

今回のプログラムでは、次のように対応しています。

double calc_ki_balance(double, double);

これはプロトタイプ宣言です。

double calc_ki_balance(double first, double second) {
    double balance = (first + second) / 2.0;
    return balance;
}

これは関数定義です。

種類役割処理の中身
プロトタイプ宣言関数の存在と形を知らせる書かない
関数定義関数の実際の処理を書く書く

プロトタイプ宣言では、引数名を省略できます。
そのため、double calc_ki_balance(double, double); のように書けます。

一方、関数定義では、関数の中で値を使うために、firstやsecondのような引数名を書きます。

なぜプロトタイプ宣言が必要なのか

C言語のプログラムは、基本的に上から下へ読まれます。

そのため、main関数の中でcalc_ki_balanceを呼び出した時点で、コンパイラがcalc_ki_balanceの存在を知らないと困ります。

result1 = calc_ki_balance(base_ki, training_ki);

この行を見たとき、コンパイラは次のことを知っておきたいのです。

知りたいこと内容
関数名calc_ki_balance
引数の数2つ
引数の型doubleとdouble
戻り値の型double

これらを先に知らせるのが、プロトタイプ宣言です。

ドラゴンボール風に言えば、修行装置が技を実行する前に、技の目録を確認しているようなものです。

技の名前、必要な気の値、返ってくる結果の種類が分かっていれば、main関数の中で安心してその技を呼び出せます。

図:プロトタイプ宣言、main関数、関数定義の関係

この図が示していること

この図では、プロトタイプ宣言、main関数、関数定義の関係を表しています。

プロトタイプ宣言では、関数の存在と形を先に知らせます。
main関数では、その関数を呼び出します。
関数定義では、実際の処理を行い、returnで結果を返します。

関数定義がmain関数のあとにある場合でも、プロトタイプ宣言を先に書いておけば、main関数からその関数を呼び出せます。

プログラムの処理の流れ

ここで、実際にプログラムがどのように動くのかを順番に見てみましょう。

まず、プログラムはmain関数から始まります。

int main(int argc, char** argv) {

main関数の中では、最初に変数を用意しています。

double result1, result2, result3;
double base_ki = 2.4, training_ki = 4.8, support_ki = 3.6;

次に、calc_ki_balance関数を呼び出します。

result1 = calc_ki_balance(base_ki, training_ki);

このとき、base_kiの2.4とtraining_kiの4.8が関数へ渡されます。

関数側では、次のように受け取ります。

double calc_ki_balance(double first, double second)

つまり、firstに2.4、secondに4.8が入ります。

そのあと、関数の中で平均値を計算します。

double balance = (first + second) / 2.0;

計算結果は3.6です。

最後に、returnで3.6を返します。

return balance;

戻ってきた値は、result1に代入されます。

result1 = calc_ki_balance(base_ki, training_ki);

この流れをまとめると、次のようになります。

手順処理内容
1main関数から開始プログラムが動き始める
2変数を用意result1やbase_kiを用意する
3関数を呼び出すcalc_ki_balanceに値を渡す
4引数で受け取るfirstとsecondに値が入る
5関数内で計算平均値を求める
6returnで返す計算結果を呼び出し元へ返す
7変数に代入result1に戻り値が入る

同じ流れで、result2とresult3にも戻り値が代入されます。

関数を使うと同じ処理を何度も使える

今回のプログラムでは、calc_ki_balance関数を3回呼び出しています。

result1 = calc_ki_balance(base_ki, training_ki);
result2 = calc_ki_balance(5.5, 7.5);
result3 = calc_ki_balance(support_ki, 6.0);

それぞれ渡している値は違いますが、実行される処理は同じです。

つまり、2つの値を受け取り、平均を計算し、結果を返すという処理を、1つの関数として使い回しています。

変数関数に渡す値戻り値
result12.4 と 4.83.6
result25.5 と 7.56.5
result33.6 と 6.04.8

関数を使わない場合、同じ計算式を何度も書くことになります。
関数を使えば、処理の中身を1か所にまとめられます。

ドラゴンボール風にたとえるなら、気の平均値を測る技を1つ覚えておけば、どんな2つの気の値に対しても同じ技を使えるということです。

関数名は役割が分かる名前にする

関数には、好きな名前を付けられます。
ただし、できるだけ役割が分かる名前にすると、プログラムが読みやすくなります。

今回の関数名はcalc_ki_balanceです。

calcは計算する、kiは気、balanceはバランスや平均のような意味で読めます。
そのため、この関数は気のバランスを計算するものだと想像しやすくなります。

関数名分かりやすさ
f短いが、何をするか分かりにくい
avg平均を求めることが分かりやすい
calc_ki_balance気の平均値を計算するイメージが伝わりやすい

学習中は、短すぎる名前よりも、少し長くても意味が分かる名前を付けると理解しやすくなります。

関数名は、処理の内容を表すラベルのようなものです。
あとからプログラムを読み返したときに、この関数は何をするのかが分かる名前にしておくと安心です。

引数名も役割が分かるようにする

関数の引数名も、分かりやすくすると読みやすくなります。

今回の関数では、firstとsecondという名前を使っています。

double calc_ki_balance(double first, double second)

この2つは、平均を求めるために受け取る1つ目の値と2つ目の値を表しています。

もっと具体的にしたい場合は、left_ki、right_kiのような名前を付けることもできます。

ただし、引数名は関数の中で使う名前です。
呼び出し元の変数名と同じである必要はありません。

たとえば、呼び出し元ではbase_kiとtraining_kiを渡しています。

result1 = calc_ki_balance(base_ki, training_ki);

関数の中では、それぞれfirstとsecondとして扱われます。

呼び出し元関数内
base_kifirst
training_kisecond

ドラゴンボール風にたとえるなら、外で持っている気の名前と、修行装置の中で受け取る気の名前は違っていても大丈夫です。
大切なのは、渡した順番に対応して値が受け取られることです。

関数は引数や戻り値を省略することもある

関数は、必ず引数や戻り値を持つわけではありません。

場合によっては、引数を受け取らない関数もあります。
また、戻り値を返さない関数もあります。

戻り値を返さない関数では、戻り値の型にvoidを使います。

関数の形意味
double calc_ki_balance(double, double)double型を2つ受け取り、double型を返す
void show_message(void)引数なしで、戻り値もない
int get_power(void)引数なしで、int型を返す

今回のcalc_ki_balanceは、2つの値を受け取り、1つの結果を返す関数です。
最初に学ぶ関数として、引数と戻り値の流れを理解しやすい形になっています。

C言語では、関数によってさまざまな形があります。
まずは、今回のように値を渡して、計算して、結果を返す関数をしっかり理解しておきましょう。

プロトタイプ宣言と関数で覚えておきたいポイント

関数を使うときは、次のポイントを意識すると分かりやすくなります。

ポイント内容
関数は処理に名前を付けたもの同じ処理を何度も使いやすくする
引数は関数へ渡す値関数の処理に必要な材料になる
戻り値は関数から返る結果計算結果などを呼び出し元へ返す
return文で値を返す関数の結果を呼び出し元へ戻す
プロトタイプ宣言で先に知らせるmain関数より後に定義した関数を呼び出しやすくする
宣言と定義は対応させる戻り値の型、関数名、引数の型をそろえる

関数は、C言語の基本文法の中でもとても重要です。

変数、計算、条件分岐、繰り返しを学んだあとに関数を使えるようになると、プログラムを部品に分けて整理できるようになります。

ドラゴンボール風に言えば、今までは修行内容をすべて1枚の巻物に書いていた状態です。
関数を使うと、気を測る技、平均を求める技、表示する技のように、技ごとに分けて管理できます。

そのうえで、main関数から必要な技を呼び出していくと、プログラム全体の流れがすっきり見えるようになります。

プロトタイプ宣言は、その技があとで登場することを先に知らせる目録です。
main関数のあとに関数定義を書く場合でも、プロトタイプ宣言を先に書いておけば、安心して関数を呼び出せます。

関数を使いこなせるようになると、C言語のプログラムは一段階レベルアップします。
同じ処理を何度も書くのではなく、名前を付けて呼び出す。
この考え方が、これからC++のより大きなプログラムへ進むための大切な土台になります。