第16回
関数あれこれ

関数の実行と値の受け取り

Cでは既存のライブラリ関数を使う他に、プログラマーが独自に関数を作って利用することが可能です。関数は処理を終えた後に値を返します。

関数から関数を呼び出す

前回(第15回「関数の宣言~ライブラリとヘッダファイル」)、文字列を縦に表示するプログラムを書き換えて、main関数の中から"tateputs"という別の関数を呼び出す形にしました(リスト1)。

これまでの例ではmain関数の中にすべての処理を記述してきました。ごく小さなプログラムならそのような書き方でも構いませんが、処理の複雑になる実用的なプログラムでは、このように『main関数からさらに別の関数を呼び出す』形とするのが一般的です。

「呼び出す」とは「関数を実行する」という意味で、ソース中に
関数名(引数);
と記述することで、それまでmain関数の中で上(ソースの先頭側)から下(ソースの終端側)に向かって進んできた処理の流れが、呼び出された関数へと切り替わります。

そして呼び出された関数内での処理が終わると、呼び出し元(main関数)へと処理は戻り、関数を呼び出した次の命令行から処理は続けられます(図1)。

処理が呼び出し先の関数に移ったり、呼び出された関数から呼び出し元へと戻ったりする様子を『制御が移行する』または『制御が移る』と表現します。

リスト1:入力された文字列を縦に表示するプログラム~ごく一般的な書き方
#include <stdio.h>
#include <string.h>

int tateputs(char *); /* 縦表示関数のプロトタイプ宣言 */

int main(void)
{
  char str[256 + 1];  /* 入力された文字列を保存 */
  int retcode;        /* シェルへの終了コード */

  printf("Input String : ");
  scanf("%s", str);

  retcode = tateputs(str);  /* 縦表示関数を呼び出す */
  return (retcode);
}

/* tateputs -- 文字列を縦に表示する */
int tateputs(char *s)
{
  if (strlen(s) <= 0)
    return (-1);  /* 引数の文字列が空ならエラーで戻る */

  while (*s) {             /* 文字がNULL(0)でない間繰り返す */
    printf("%c\n", *s++);  /* 1文字に改行を付け足して出力 */
  }
  return (0);
}


値を返す

呼び出された関数内で処理が終わって呼び出し元に制御が移ることを『戻る』と表現します。戻るときには、呼び出し元に何らかの値を渡すことができ、これを『値を返す』と言います。返す値は『戻り値』と呼びます ※1

値を返す関数では、その関数の宣言と定義で返す値の『型』を明示しなければなりません。値を返さない場合もvoid型として宣言/定義します。つまり、Cの関数は必ず値を返すことが前提で、値を返さない場合は「返す値がない」ことを示す型としてvoidを用いる訳です。

a)値を返さない場合
void <関数名> (<引数>);
例) void foo(int num);

b)値を返す場合
<戻り値の型> <関数名> (<引数>);
例) long bar(int num);
値を返さない関数(void型の関数)では、関数内で処理が終わって終端の } にたどり着けば、自動的に呼び出し元に制御が移ります。処理の途中で強制的に終了させて呼び出し元に制御を戻したい場合は、return();と記述します。

値を返す関数では、処理を終えるときに必ずreturn命令を使い、( )内に戻り値を記述する必要があります。戻り値の型は、その関数を定義したとき先頭に記述する関数の型でなければなりません。

英語では"return value"と表記されており、C言語のバイブルとも呼ばれるカーニハンとリッチーの書いた「プログラミング言語C」では『返り値』と訳されていました。筆者の記憶では、これを『戻り値』と表現したのはVisualBasicの日本語版ヘルプではないかと思います(違っているかも知れません)。まぁ、「かえりち」と入力してしたら多くの場合「返り血」と変換されてなんだか物騒なので、「戻り値」の方が安心かと思います(笑)

戻り値の受け取り方

値を返す関数を呼び出した呼び出し元の側では、関数からの戻り値を受け取らなければなりません。戻り値は代入演算子=を使って変数に格納するか、あるいは式の中で直接関数の呼び出しを記述しても構いません。

例えば、関数fooがint型の値を返すとします。関数の宣言は以下のようになります。
int foo(...);
これを呼び出す側では、戻り値を以下の2通りの方法で受け取れます。

a)戻り値を変数に格納する場合
int answer;
    :
answer = foo(...);

b)戻り値を式の中で使う場合
if (foo(...) == 100) {
    :
b)のような書き方では、まず関数fooが呼び出され、その結果の戻り値が定数の100と等しいか比較されます。

リスト1の
retcode = tateputs(str);  /* 縦表示関数を呼び出す */
のところではa)の方法を用い、tateputs関数を呼び出してその戻り値を変数retcodeに受け取っています。