Phase #06

関数の基礎


1.関数とは

まず関数とはどういうものなのか見ていきましょう。関数と聞くとまず最初に思い出すのが「数学」の関数だと思います。実はC言語の関数も数学の関数も考え方としては同じなのです。どういうことなのか、数学の関数がどういう働きをしているか見ながら説明します。
例えばこんな関数があったとします。

 f(x) = 3x2 - 4x + 7

この関数 f(x)は x の値を具体的に入れてあげれば関数の答えも決まります。x = 10 とすれば、

 f(10) = 267

となりますね。C言語の関数も同じような働きをします。関数ある値を与える(入力)と何らかの処理をして(機能)結果を関数の値(出力)として返すわけです。C言語の関数には値を返さない関数もあります。こういう関数は関数の処理自体に意味があるのでとりわけ答えを返す必要がないのです。
ブラックボックス さて、もし f (x) = 3x2 - 4x + 7 だと知っていて f(x) に xを代入すれば自動的に答えが求められるのであれば f(x) がどうやって計算をしているかという過程は知らなくても使う側からみれば問題ないですよね?つまり、その関数の働きが分かっていれば関数を「ブラックボックス(*)」として扱い内部のことまでは気にしなくてもいいのです。大胆な例を挙げるとすれば、普段パソコンを使っているときにその内部でパーツがどんな風に動作しているかいちいち考えなくても全然問題なく使っていられますよね?それと同じなのです。
このように関数はブラックボックスと考えることができることが分かったと思うのですが、ブラックボックスだとどのようなメリットがあるのでしょうか?例えば・・・

> プログラムが分かりやすくなる
> 複数人で分担してプログラムできるようになる
> 一度作ったものを再利用しやすくなる

など、効果は絶大です。中でも再利用できるという点はプログラマが受ける恩恵はとても大きいのです。この講座の最初にLSI-Cというコンパイラを入手したと思いますが、このコンパイラについてくるものに標準ライブラリというものがあります。これは基本的な入出力や文字列操作などを簡単に使えるようにした関数の集まりなのです。今までのサンプルで画面に文字を表示しましたが、これも標準ライブラリの中の printf という関数を使って表示していたのです。
標準ライブラリ以外にも様々な人たちがグラフィックスや複雑な統計処理などのライブラリを提供しているので簡単に機能を拡張することが可能です。
このようにC言語をプログラムするということはさまざまな関数のコンビネーションで機能を作り出すことなのです。関数がどんなものか分かってもらえたと思うのでその中身を見ていきましょう。
 [ 例 ] 関数を使って3*x2 - 4*x +7 を計算する例  ex6-1.cGet! ソースファイル

2.関数の定義


関数の中で実行する実行文を記述する部分が関数の定義部分です。
 型宣言子 関数名 (仮引数の並び) 
 {
  関数内部の変数の宣言;

  実行文;
  ・・・・
 }

○型宣言子は、値を返す関数の場合はそのデータ型、値を返さない場合は void と記述。
 void型「データ型のないことを表す(=値のやりとりをしない)型」で少し特殊な型。
 型宣言子を省略したときはint型として扱われる。
仮引数呼び出し側からデータを受け取るときに利用。
○仮引数の並びでは仮引数自体の宣言もする。仮引数は型宣言子を付けて宣言する。
 複数仮引数があるときは「 , 」で区切る。
○仮引数がなくても ( ) は必要。引数がないことをはっきりさせるために (void) と記述したほうが良い。
○最も小さい関数は 関数名 ( ) { } となる。この場合何もしない。
○関数内部で使用する仮引数以外の変数は、実行文の前でまとめて宣言を行うのがよい。
○関数定義の中では他の関数は定義できない。

関数を定義するときには読みやすいプログラムにする努力をします。関数名は関数の働きが分かるような適切な名前にします(これについては後述するかも知れません)。コメントで関数の働きを説明しておくことも有効な手段です。引数や戻り値(=関数の返す値)なども説明しておくのがいいでしょう。これらの関数を説明するコメント文関数ヘッダと読んだりもします。関数ヘッダの記述スタイルは自由ですが、自分なりに工夫して分かりやすいスタイルに統一するようにしましょう。

 [ 例 ] 関数ヘッダの例
 /* -------------------------------------------------
  関数名 : funcx
  機能  : 3^x - 4 * x + 7 を計算する
  引数  : int x >> 入力データ
  戻り値 : int y >> 計算結果
  --------------------------------------------------- */

 int funcx (int x)
 {
  ・・・・

コメントについて


今まで特に説明しませんでしたが、ここでしっかり説明しておきましょう。
コメント/* と */ で囲まれている部分のことです。コメントはコンパイルするときにプログラムとして扱いません。何のためかというと説明を書いておいたりするためのもです。複数行にわたっていても構いませんが、入れ子にはできません。
この講座の中ではコメントは分かりやすいように青色に色分けしてあります。

【正しいコメントの例】
int val; /* 変数の宣言(行の途中からでも記述できる)*/
/* コメントの例
このように複数行にわたっていても問題ない。
val = 5; ← 正しい文でもコメント中の文はコンパイルされない(エラーではない) */

【間違ったコメントの例】
/* 良くないコメントの例
 /* 入れ子の場合だとこの部分でコメントが終わりだと認識されてしまい → */

*/ ←この部分でエラーになる

もう一つ // というのがあってこれはその行の // 以降をコメントと見なします。しかし、// をコメントとして認識しないコンパイラもあります。(LSI-Cも認識しない)

3.関数の呼び出し


関数は定義しただけでは意味がありません。関数を呼び出すことで始めて関数は処理されます。
 関数名 (実引数の並び) 

実引数関数側にデータを渡すときに利用。
○実引数の並びは、仮引数と同じ個数で対応する引数の型が一致していないといけない。
 複数個の場合は仮引数同様「 , 」で区切る。実引数の並びの中では型宣言する必要はなく、値だけでよい。
○実引数がなくても ( ) は必要。呼び出し側は( ) のみで、(void) とはしない。
○関数の呼び出しは何処からでも何回でもできる。
○呼び出された関数の処理が終わると、呼び出し側に処理が戻る。

一般的に関数を記述する際にはプロトタイプ宣言と呼ばれるものします。コンパイルをするときにコンパイラはファイルの先頭から順番にコンパイルします。この時に関数の定義が呼び出しよりも先にないと関数を見つけられずコンパイルエラーとなります。これに従うと関数を使ったプログラムはこんな感じになります。

[ 例 ] プロタイプ宣言をしない場合の例  ex6-2.cGet! ソースファイル
int funcx (int x)
{
 int y; /* 関数内部で使う変数の宣言 */

 y = 3 * x * x - 4 * x + 7; /* 3*x^2 - 4*x + 7 の計算 */
 return (y); /* 計算結果を関数の戻り値として返す */
}

void main (void)
{
 int dat, ret;

 for (dat = -10; dat <= 10; dat++) {
  ret = funcx (dat); /* 関数funcxの呼び出し */
  printf ("f(%d) = %d\n", dat, ret);
 }
}

しかし、関数本体はあくまでも「ブラックボックス」です。ブラックボックスがいきなりでてきてしまうとプログラムとして分かりづらくなります。そこで、関数の呼び出し側よりも先に関数の型と引数の型宣言だけあらかじめ行っておくのです。これが関数のプロトタイプ宣言なのです。
プロトタイプ宣言の仮引数では引数名を省略してデータ型の並びだけにしても構いません。注意する点はプロトタイプ宣言は文なので、最後に「 ; 」を忘れないことです。

[ 例 ] プロタイプ宣言をした場合の例  ex6-3.cGet! ソースファイル
/* ---< プロトタイプ宣言 >--- */
int funcx (int x);

void main (void)
{
 int dat, ret;

 for (dat = -10; dat <= 10; dat++) {
  ret = funcx (dat); /* 関数funcxの呼び出し */
  printf ("f(%d) = %d\n", dat, ret);
 }
}

int funcx (int x)
{
 int y; /* 関数内部で使う変数の宣言 */

 y = 3 * x * x - 4 * x + 7; /* 3*x^2 - 4*x + 7 の計算 */
 return (y); /* 計算結果を関数の戻り値として返す */
}


Jump to Phase #05-2Phase #05-2: 配列(2) へ
Phase #06-2: 関数の基礎(2) へJump to Phase #06-2

△戻る
▲トップに戻る