C言語入門 第7回 関数宣言と値を返す関数
2014年10月より個人の方を対象に、Study C無料提供を開始しました。
C言語を勉強中の方は、学習・教育に最適なC言語インタープリタのStudy Cを使ってみてください(個人の方は無料です)。
大学・高専・高校などの教育機関での採用実績も多数あるロングセラー商品Study Cが、個人向けに無料提供を始めました。
インタープリタの手軽さに加え、ゲームや3Dタートルグラフィックで楽しく勉強したりと、C言語の学習を強力にサポートします。
また、このようなボタンの用意されているページでは、掲載しているプログラムをStudy Cに直接ロードし実行したりすることができます。
Study C無料利用についての詳細は、このページを参照してください。
C言語の特徴は、プログラムを関数という単位に分割して作成することです。
たとえばプログラムの中に似た部分が何回も出現する場合、その部分を抜き出して関数にします。
変数nに15 ,30, 52を順番に代入して、3本の棒グラフ('*'で)を表示してみましょう。
main() { int i, n; n = 15; for (i = 0; i < n; i++) { printf("*"); } printf("\n"); n = 30; for (i = 0; i < n; i++) { printf("*"); } printf("\n"); n = 52; for (i = 0; i < n; i++) { printf("*"); } printf("\n"); }
*************** ****************************** ****************************************************
このプログラムでは、3か所にほとんど同じ部分が出現しています。このような共通部分を取り出して1つの関数形式にすればプログラムは見やすくなります。
中学校の数学で習う関数の基本はy = f(x)という形式ですが(xに何かの値を与えると、それに対応する値yが返ってきます)、C言語の関数は値を返さなかったり、複数の値を受け取ることができます。
関数の形式は次のようになっています。
関数名(値を受けとるために使う変数名) 値を受け取るために使用する変数の変数宣言; { ・値を受け取る変数以外にこの関数内で使用する変数が あれば、その変数宣言を行う。 ・関数が実行するプログラム内容。 }
この形式は受け取る値が1個、返す値が何もない場合に使用できます。この形式に従って'*'で1本の棒グラフを表示する関数を作ってみましょう。
まず関数名を決める必要があります。棒グラフを表示するという意味をprint_barという関数名にしてみましょう(関数名も変数名と同じ規則でわかりやすい名前をつけます。barとは棒という意味です)。
次に受け取る値を決定しますが、この関数は棒グラフの高さ('*'の個数)を受け取り、その数だけ'*'を表示するのが最も適切でしょう。
値を受け取る変数名はもとのプログラムと同じくnとします。
これまでの部分を形式にあてはめて書くと次のようになります。
print_bar(n) ←ここにセミコロンはつけません int n; { 関数が実行するプログラム内容 } 注)別の関数宣言の方法 これと同じ意味の関数宣言をC言語では次のように記述することもできます。 print_bar(int n) { ... } print_bar(n) { ... } 入門書などでこのような記述があるかもしれませんが、どれも正しい記述方法です。
print_bar関数は変数nに代入された数だけ'*'を表示し最後に改行する関数とします。 プログラムを完成させると次のようになります(変数nには呼び出す部分で15, 30, 52などの値がセットされるのでn = ~;のような代入部分は必要なくなります)。
print_bar(n) int n; { int i; for (i = 0; i < n; i++) { printf("*"); } printf("\n"); }
これでprint_bar関数は完成したので、この関数を呼び出してみましょう。呼び出し方法の 形式は次のとおりです。
関数名(受け渡す値);
したがって、呼び出す関数がprint_barの場合はprint_bar(15);などとなります。 関数を使って3本の棒グラフを表示するプログラムを作成してみましょう。
main() { print_bar(15); print_bar(30); print_bar(52); } print_bar(n) int n; { int i; for (i = 0; i < n; i++) { printf("*"); } printf("\n"); }
*************** ****************************** ****************************************************
参考)
このプログラムを実行すると"WARNING(1202) プロトタイプ宣言なしに関数が呼ばれています."という表示してから実行結果が表示されると思います。
WARNING...はエラーではないけれども注意したほうがよい個所をプログラマに知らせるため、Study Cが表示するメッセージ(警告)です。
これに対してエラーメッセージはERROR...という形式で表示され、それ以上プログラムを実行することが不可能なためメッセージ表示後処理は中止されてしまします。
このプログラムを実行したときに表示される警告は、コンパイラやインタープリタが"見知らぬ関数に出会いました。どのような呼び出しかたが正しいか解らないので、規定された方法で呼び出しておきます。間違った呼び出しかたでもしりませんよ。"とプログラマに知らせています。 たしかにコンパイラでは、この警告を無視していると重大なプログラムミスをおかしてしまうことがあります。 しかし、Study Cを使っている分には正しく呼び出しています。これではインタープリタを卒業してコンパイラを使うようになった時に困ることがあるとので、Study Cはコンパイラが表示するのと同じようなメッセージを表示します。 この警告が表示されなくするにはつぎの行を関数呼び出しの前に追加します。
int print_bar(int);
この関数宣言のできそこないのような行をプロトタイプ宣言と呼びます。 プロトタイプ宣言つきでプログラムをもう一度書き直すと次のようにまります。
int print_bar(int); main() { print_bar(15); print_bar(30); print_bar(52); } print_bar(n) int n; { int i; for (i = 0; i < n; i++) { printf("*"); } printf("\n"); }
参照)プロトタイプ宣言について
このように、関数を使用すればプログラムを見やすく書き換えることができます。
print_bar関数とmain()の部分を見比べてみると、類似した形式になっていることに気がつくと思います。
実はmainは受け渡される値(print_barではnに相当するもの)が何もない形式の関数の一つなのです。
関数mainが他の関数(print_barなど)と異なる点は、プログラムの実行を開始(runコマンドで)したとき、最初に呼ばれるという点です。
また、printfもprint_barと同じような形式で呼び出されており、同様に一つの関数です。
しかし、printfはprint_barのように、その関数の実体が定義されていません。
printfはC言語であらかじめ用意されている関数です。C言語にはprintf以外にも多数の便利な関数があらかじめ用意されています。
前プログラムのprint_bar関数を空白の後に'*'を1個表示する関数(print_dot)に替えて1, 4, 9, 16, 25, 36, 49, 64の位置に'*'を表示するように変更してみましょう。 空白の後に'*'を表示する関数は点を表示するという意味でprint_dotとします。
int print_dot(int); main() { print_dot(1); print_dot(4); print_dot(9); print_dot(16); print_dot(25); print_dot(36); print_dot(49); print_dot(64); } print_dot(n) int n; { int i; for (i = 0; i < n-1; i++) { printf(" "); } printf("*\n"); }
* * * * * * * *
この例は1の2乗、2の2乗、3の2乗、...つまりy = xの2乗の曲線を表示させているものです。
このプログラムの中でprint_dotを何回も呼び出している部分がありますが、この部分は前のC言語入門で勉強したforループを1つ使うことですっきりした形にすることができます。
for文を使った表現に変えるこつは、増加していく値を見つけだしてfor文の形式2で使用する変数に関連づけさせることです。
この例で増加している値はprint_dot関数に与えている値(1, 4, 9, 16, 25, 36, 49, 64)です。
しかし、これらの値は1ずつ増加していないので、forの変数を直接利用することはできません。
前の例のprint_dotに1, 2, 3, 4, 5, 6, 7, 8をforループの変数を使って与えるように変更してみましょう。
int print_dot(int); main() { int x; for (x = 1; x <= 8; x++) { print_dot(x); } } print_dot(n) int n; { int i; for (i = 0; i < n-1; i++) { printf(" "); } printf("*\n"); }
* * * * * * * *
次にprint_dotへ2, 4, 6, 8, ..., 16を与えるようにしてみましょう。for文は、いまの例と同じままでprint_dot呼び出しの()内だけを変更します。
int print_dot(int); main() { int x; for (x = 1; x <= 8; x++) { print_dot(x*2); } } print_dot(n) int n; { int i; for (i = 0; i < n-1; i++) { printf(" "); } printf("*\n"); }
* * * * * * * *
この例を参考にして1の2乗、2の2乗、3の2乗、...(y = xの2乗)の曲線を表示させた前のプログラム例をforループで書き換えて見ましょう。
int print_dot(int); main() { int x; for (x = 1; x <= 8; x++) { print_dot(x*x); } } print_dot(n) int n; { int i; for (i = 0; i < n-1; i++) { printf(" "); } printf("*\n"); }
* * * * * * * *
いままで使用した関数はすべて値を返さない形式のものでしたが、C言語では一般の関数のように値を返すことができ、返された値を呼び出した場所で利用することができます
(返された値を利用するか無視するかは自由です)。
まず、関数の定義は異なり、次のようになります。
関数名(値を受け取るために使う変数名) 値を受け取るために使用する変数の変数宣言; { ・値を受け取る変数以外にこの関数内で使用する変数があれば その変数宣言を行う。 ・関数が実行するプログラムの内容 return (返す値); }
この形式の主な用途としては、関数にある値を与えその値をもとに関数内で計算を行い結果を返す使い方です。次にこの例を示します。
int sankaku_menseki(int, int); main() { int menseki; menseki = sankaku_menseki(5, 6); printf("(5,6) -> %d\n", menseki); menseki = sankaku_menseki(8, 7); printf("(8,7) -> %d\n", menseki); } sankaku_menseki(teihen, takasa) int teihen, takasa; { int s; s = teihen * takasa / 2; return (s); }
(5,6) -> 15 (8,7) -> 28
このsankaku_menseki関数は、与えられた三角形の底辺から三角形の面積を返す関数です。
呼び出し側ではmenseki = sankaku_menseki(5, 6);のように返される値を変数に代入して利用することができます。
関数は返される値は無視して使用しないこともできます。たとえば、いままで使ってきたprintf関数は出力(表示)した文字数を返していますが、常に利用してきませんでした。
次のプログラムはprintfが返す値を表示させた例です。
main() { int len; len = printf("abcde"); printf("\n%d\n", len); len = printf("abc\ndef"); printf("\n%d\n", len); }
abcde 5 abc def 7
このプログラムは1番目と3番目のprintfが返す値を表示しています。改行文字である\nも1文字として数えられます。