プログラム言語 II TOP

関数(1)

例題6.1


1. 関数とは
簡単にいうと,"与えられたデータを使って,何かの計算(処理)を行い,結果を出すもの"が関数です。

電卓で,数値を設定して,平方根(ルート)のキーを押すと,設定した数値の平方根が表示されます。これは,電卓には平方根を求める関数があり,それを使っているのです。

このように関数を使うことを"関数を呼び出す"といいます。関数は呼び出されると,計算結果を(呼び出したものに)返します。


処理内容が複雑になれば,プログラムは大きくなります(行数が増える)。当然,そのようなプログラムを書いたり,読んだりすることは大変なことになります。

そこで,何らかのまとまりごとに分けて,プログラムを作ることになります(本などでも,章や節に分かれていますよね。それと同じようなことです)。Cでは,プログラム全体を機能(どんなことを行っているのか)で分けて,それぞれを関数(function)という小さなプログラムで作ります。そのような関数が集まって,プログラム全体となります。


プログラムを,データを読み込み部分,処理(実際にはさらに分ける)部分,結果を出力する部分の3つの関数に分割する。


さて,電卓の利用者は,どのようなアルゴリズムで平方根が求められているのか知りません(分かりません)。このように関数の中は,外から見えないようになっているのです。


関数には名前をつけることができます。実は,今までのプログラムは main という名前の関数ひとつからなるプログラムだったのです。

ひとつのプログラムがいくつかの関数からできているとき,main という名前の関数からプログラムの実行が始まります。

また,printf とか scanf も関数です。入出力という機能を取り出し,あらかじめ用意しておいたものです。このように,よく使われそうなものはあらかじめライブラリ関数(library function)として用意されています。そして,ライブラリ関数を使うときには,#include というおまじないが必要になります。

関数は,それぞれの独立性が高くなるように,つまりある関数を書き換えても,他の関数への影響が小さくなるようになっています。そこで,ある関数はプログラマAが作成し,別の関数はプログラマBが作成するというようなことが容易にできます。


[ここまでのポイント]
 関数
 関数を呼び出す
 プログラムを分割
 関数に名前を付ける(例 main)
 ライブラリ関数


2. 関数の例

 #include <stdio.h>
 main()
 {
  int i, j;
  int func(int);        関数原型(関数定義が後にあるから必要)
                 関数 func は,整数型の引数をひとつもち,整数型の結果を返す
  i = 1;
  j = func( i );        関数呼出しと実引数 i (変数 i の値をデータとして,関数にfuncに渡す)
                 関数での処理結果が j に代入される
  printf("%d %d\n", i,j);
 }

 int func(int a)        関数 func の定義と仮引数 a(呼び出されたときに受け取ったデータを a に格納する)
 {
  int b;
  b = a+1;
  return b;          b の値が関数の処理結果(返却値)
 }

実行結果
 1 2              変数 i の値が 1で,j の値が 2


3. 実引数と仮引数
2 の例のように,関数にデータを渡して処理させることができます。データの受渡しに使うものが引数です。引数は次に説明するように2種類あります。

関数を呼び出すときに,実際のデータが格納されている変数を実引数と呼びます。それに対して,関数を定義するとき,つまりその関数のプログラムを書いているときに,"データを渡されたら,それを格納する変数"のことを仮引数と呼びます。

つまり,関数を呼び出す方がデータを実引数に格納し,関数を呼び出します。すると,呼び出された関数は,実引数の値が仮引数に格納されているものとして処理を行います。


さて,関数の中で,仮引数の値を変更するとどうなるのでしょうか?対応する実引数の値が変わる?いいえ,変わりません。仮引数の値を変更しても,その影響は関数の中だけで,呼び出した方には影響しません。

Cの引数は,呼び出す方から関数へデータの値を渡すだけの働きしかありません。このように値を渡すだけの方法を値渡し(call by value)と呼びます。

先の関数 func を,次のように変更してみます:

 int func(int a)
 {
  int b;
  a++:            仮引数の値を変える
  b = a;
  return b;
 }

この関数を
 j = func( i );
により呼び出しても,実引数 i の値は,func の呼出し前と変わりません。つまり,実行結果は変わりません。


4. 返却値
関数で処理した結果を,呼び出した方に返せないと困ります。その値を関数の返却値と呼びます(数学の言い方では,関数の値,すなわち関数値となります)。

 return 返却値;

により,関数の実行を終了し,指定された返却値を返します。

返却値が省略されている場合は,単に関数の実行を終了し,呼び出した方の実行を再開するだけです。


[ここまでのポイント]
 関数原型
 引数 − 仮引数と実引数
 返却値


5. 例題6.1
税率や税金の計算方法が,ちょっと前のものです。でも,今の日本とは書いていないので,そのような税体系の某国ということにしましょう。


6. 演習
現在の日本のように,5%の消費税だけとした関数CalcTaxを作りなさい。この場合,人数は不要となるが,mainの方を変えずに済ますために残しておくものとする。


7. 補足
a) 関数原型(関数プロトタイプ function prototype)
関数原型は,関数名,引数の数と型,返却値の型をコンパイラに指示するもので,この情報によりコンパイラは関数呼出しを正しく処理することができるようになります。

ところで,コンパイラは関数の定義を処理すれば,上記の情報を知ることができます。つまり。ある関数の呼出しが,その定義よりも後にある場合は,関数原型なしでもコンパイラは関数呼出しを正しく処理することができます。そこで,2の例で関数 func の定義が関数 main より前にあれば,関数 main の中にある関数原型は不要となります(あってもかまいません)。

b) 参照渡し(call by reference)
仮引数の値を変えると,実引数の値が変わるような引数の使い方ができるプログラム言語もあります。そのような使い方のひとつに参照渡しがあります(教科書p.107参照)。


プログラム言語 II TOP