4.関数の戻り値

関数は処理の結果を呼び出し側に返すことができます。戻り値を返すには return文を使います。
 return (式) 

○式の値を関数の値、戻り値として返す。
○呼び出し側に制御が戻る。

【例】
return (val);← 変数valの値を返す。
return (0);← 数字を直接書いてもよい。
return (3*x*x - 4*x + 7);  ← 演算式も可。
return;← 戻り値は不定。制御が戻るだけ。

関数の戻り値としてよく使うものに関数の終了状態があります。関数内で何らかのエラーが発生したときに呼び出し側にそれを知らせて対策をしないといけないかもしれません。
こういった「異常」を示す値には関数の戻り値として普通ではあり得ない値を使います。例えば、正数を返す関数ならば負数、例えば -1 といった具合です。

5.特別な関数 - main関数


この章で関数について説明しましたが、実は今までのサンプル中でも1つだけ関数を使っています。サンプルのソースを見れば分かるのですが、実行文は main ( ) で始まってますよね?これがC言語における特別な関数「main関数」なのです。
main関数が特別なのは次のような理由からです。

1.プログラム中には main( ) が必ず必要であること。
 → 他の関数はなくても問題ないのですが、main( )だけはプログラムに必ず1つ必要です。
2.プログラムは main( ) から開始されること。
 → 実行は main( ) から開始され、原則的には main( ) を終了すればプログラムも終了します。

今までのサンプルでは main( ) として使ってきましたが厳密に関数を定義するならば void main (void) です。では、main( ) は引数や戻り値を持てるのでしょうか?これは他の関数同様持てますが、ある一定の形式のみに限定されます。この引数や戻り値を使った例は機会があれば紹介します。

6.便利な使い方 ー 再帰呼び出し


先ほど「関数の呼び出しは何処からでも何回でもできる」と説明しました。つまり、関数の中で自分自身を呼び出すこともできます。このような呼び出し方法を再帰呼び出しといいます。再帰呼び出しを使うとプログラムが簡潔になり分かりやすくなることがあります。
また余談ですが数学には再帰を使ったいろいろと興味深いものがあります。右のイメージは「ドラゴン曲線」と呼ばれるもので、これも再帰を使って描かれたものなのです。
ドラゴン曲線(20.3k)
ここでは講座の最初に紹介した階乗の計算を例として見ていきます。階乗は別に再帰呼び出しを使わなくてもwhile文などの繰り返し文でもかけます。こんな感じになるでしょう。

[ 例 ] 再帰呼び出しを使わないで階乗を計算する例  ex6-4.cGet! ソースファイル
/* ---< プロトタイプ宣言 >--- */
int fact(int val);

void main(void)
{
 int dat, ret;

 for (dat = 1; dat <= 5; dat++) {
  ret = fact (dat); /* 関数factの呼び出し */
  printf ("%d! = %d\n", dat, ret);
 }
}

int fact(int val)
{
 int tmp = 1; /* 関数内部で使う変数の宣言 */

 while (val > 0) { /* 0より大きい場合はループ */
  tmp *= val; /* valからval - 1,val - 2,・・・,2,1まで */
  val--;    /* 順番に乗算していく */
 }
 return (tmp); /* 結果を戻り値として返す */
}

では、これと同じ働きを再帰呼び出しで実現してみましょう。まずは、階乗の計算のされかたをよく見てみましょう。

1 ! = 1
2 ! = 1 * 2
3 ! = 1 * 2 * 3
4 ! = 1 * 2 * 3 * 4
 ・・・・

よく見てみると次のように書き換えができますね。

1 ! = 1
2 ! = 1 ! * 2
3 ! = 2 ! * 3
4 ! = 3 ! * 4
 ・・・・

つまり、n の階乗は n が1以外の時は n * (n - 1) !、1 の時は 1 で計算できます。n = 1以外では n * (n - 1) ! で計算できるので、このような式を再帰パターン、n = 1 のときに再帰呼び出しを抜けるのでこれを境界条件、抜け出るときに値として 1 を返せばいいのですが、このような値を境界値と呼びます。
再帰呼び出しのプログラムを組む上でのキーポイントは再帰パターンを見つけることです。見つけてしまえばプログラムを組むのは簡単なのです。これをふまえると次のように書けます。

[ 例 ] 再帰呼び出しを使って階乗を計算する例  ex6-5.cGet! ソースファイル
/* ---< プロトタイプ宣言 >--- */
int factr(int val);

void main(void)
{
 int dat, ret;

 for (dat=1; dat<=5; dat++) {
  ret = factr (dat); /* 関数factrの呼び出し */
  printf ("%d! = %d\n", dat, ret);
 }
}

int factr(int val)
{
 int tmp;

 if (val == 1) {
  return (1);  /* valが1の時は1を返す */
 } else {
/* 1以外の時は val-1を引数にfactrを再帰的に呼び出す */
/* さらにその戻り値とvalをかけて戻り値とする */

  return (val * factr(val - 1));
 }
}


○ソースファイルのダウンロード○
この章に使った例のソースファイルを1つにまとめて圧縮してあります。
 【中に含まれているソースファイル】
  ex6-1.c ex6-2.c ex6-3.c ex6-4.c ex6-5.c

ソースファイルのダウンロードexample06.lzh (2927 Bytes)

再帰呼び出しを使ってプログラミングするときには再帰から抜け出すための条件である境界条件をしっかり設定することです。これを間違えると無限ループに陥ったりします。

ここまで関数の基礎的な部分を見てきました。今後の章でも関数の応用的な使い方などがでてきますので、しっかりおさえておいて下さい。


Jump to Phase #06-1Phase #06-1: 関数の基礎(1) へ
Phase #07-1: コンパイルの過程(1) へJump to Phase #07-1

△戻る
▲トップに戻る