関数とは

[PDF版] [関連課題]

プログラミング言語のもっとも重要な要素の1つが関数です。数学での関数は、 パラメータがあり、それを何らかの値にマッピングするというものでした。た とえば、

f(x) = 2x+1
とかというやつです。これは、xをパラメータとしてxに 2とかけて1をたした値とするという関数です。もちろん、プログラミング言 語での関数も、これと同じことができます。たとえば、f(x)は以下のようにか くことができます。関数の値を「返す」ために、return文を使います。
int foo(int x)
{
      return 2*x+1;
}
呼び出しは、以下のように書きます。
int main()
{
     int z;
     z = foo(10);
     printf("z=%d\n",z);
}
以上のプログラムでは、f(10)の値を計算しています。

プログラミング言語での関数は数学での関数のような役割以外にもっと重要な 役割をもっています。それは、機能を一まとめにしておくという役割 です。Cでは「関数」(function)と呼んでいますが、そのた め、他の言語では関数以 外にも「手続き」(procedure)と呼んでいる場合もあります。たとえば、 printfやscanfも関数です。入力や出力したりという機能を関数(手続き)と してまとめておけば、それをいちいち書かなくても、それを「呼び出す」こと によって、その機能を使うことができるようになります。関数がなければ、同 じような操作を繰り返して書いておかなくてはなりません。これでは、大きい プログラムは作ることができません。

実際、プログラムは関数の集まりとして作られています。Cのmain()も関数です。いろいろな動作をするプログラムはこの機能を持つ関数の集まりとして作ることによって、実現されています。

関数定義と関数呼び出し

関数の定義は以下の形式で書きます。

関数の返す値のデータ型 関数名(パラメータのデータ型 パラメータ名、….)
{  
     /* あれば、変数の宣言 */
   …関数のプログラム…
   return 関数から返す値の式;
}
returnから始まるreturn文は、最後でなくてもかまいません。return文が実行 された時点で、関数の実行は終了し、return後に書かれた式の値が呼び出し側 に値が返されます。関数名の規則は変数と同じで英数字と_だけが使えます。 「関数の返す値のデータ型」は省略できますが、その場合関数の返す方はint (整数)と解釈されます。例えば、今まで書いてきたmainはその一つです。し かし、なるべくつけるようにしましょう。

関数呼び出しは、

関数名(引数の式、….)
と書きます。これは関数の返す値のデータ型を持つ式として使えます。例えば、
x = foo(1,y)+1;
というようの式の中で使えます。関数にパラメータとして与える値のことを 「引数」(ひきすうとよむ)といいます。関数呼び出しが行われると、引数の 式の値が対応するパラメータの変数として参照することができます。図に関数 の実行の流れを示します。関数の実行が終わると、実行の流れは関数呼び出し のところに戻り、関数呼び出しの式の値として、関数の返した値が参照されま す。

関数の呼び出しをする場合は、その前に関数の型の宣言が必要です。これは、 関数の宣言の本体({ }以外の部分)を除いたものです。

関数の返す値のデータ型 関数名(パラメータのデータ型 パラメータ名、…);
最後の;をわすれないでください。これを関数のプロトタイプ宣言と いいます。 これは関数の返す値とそのパラメータのデータ型をあらかじめ宣言しておくも のです。関数定義の外に、通常プログラムのはじめに書いておきます。返す値 のデータ型が整数の場合には省略できますが、これもなるべく行うようにして ください。ちなみに、#includeでstdio.hなどを読み込んでいるのは、ライブ ラリ関数のプロトタイプ宣言などを読み込んでいます。また、sinやcosなどの ライブラリを使う場合には、このライブラリ関数のプロトタイプ宣言をしてあ る、math.hをincludeする必要があります。

関数の書き方についてまとめておきましょう。

下の例に、2つの数の大きいほうを返す関数を使う例を示します。
#include <stdio.h>

int imax(int a,int b);

main()
{
   int x,y,z;
   scanf("%d",&x);
   scanf("%d",&y);
   z = imax(x,y);
   printf("max is %d\n",z);
}

int imax(int a,int b)
{
   if(a > b) return a;
   else return b;
}
関数がなぜ必要なのかを例で考えてみることにしましょう。関数はある機能を 一まとめにしたものです。例えば、4つの数の最大値を求めたい場合、if文を たくさん書くよりも、
  imax(imax(1,3),imax(5,4))
と書いたほうが、簡単にすみます。

手続きとしての関数

関数は機能を一まとめにしておくものですので、値を返さない場合もあります。 その場合は、関数の返すデータ型にvoidと書きます。これは、値を返さないも のという意味です。

void 関数名(パラメータのデータ型 パラメータ名、…)
{   
      /* あれば、変数の宣言 */
   …関数のプログラム…
   return;
}
この場合はreturnには式をつけません。また、関数は最後まで実行すると自動 的にreturnしますので、最後でreturnする場合には書かなくてもかまいません。 この場合、値が返されないので、関数の呼び出しは式の中に書かずに、文とし て
  関数(引数、…);
と書きます。

いろいろな変数:局所変数、大域変数、静的変数の使い方

これまで、変数はすべて関数の中に宣言してきました。関数の中で宣言されて いる変数を局所変数(local variable, 自動変数:automatic variableと もいう)といいます。局所変数(local variable)は、関数の中で一時 的に使うデータを格納しておくのに使います。それに対し、関数の外で宣言さ れた 大域変数 (global variable) はプログラム全体で使うデータを格納しておくために使い ます。

関数は数学の関数のように、パラメータのみを使って、値を計算するだけでは ありません。通常、関数はある機能ごとに作り、プログラムをわかりやすく部 分部分に分けて書くために使います。大きなプログラムを作る場合にはこのよ うにして部分に分けてかくことは非常に重要です。例えば、カウンタがあり、 それを増やしたり(increment)減らしたり(decrement)という関数を次のよう にしてつくっておきます。

int counter;
void increment() { counter += 1; }
void decrement() { counter -= 1; }
これは非常に簡単な例ですが、このようにしておいて、この関数を使っておけ ば、counterに対する演算をいろいろなところにかくよりも、関数の名前、つ まりやりたいことの名前でかくことができるようになり、プログラムがわかり やすくなります。この関数は値をかえしませんが (voidをつかった手続き)、 手続きに名前をつけて使うことができるようになります。

多くの局所変数は、大域変数でもよい場合があります。一時的に大域変数をつ かってもよい場合です。例えば、int t;と大域変数を宣言しておけば、

int foo(int x,int y)
{  
    t = x + y; 
    return t;
}
でもかまいません。しかし、大域変数は他の関数でも使われる可能性があるので、
int goo(int x, int y) 
{ 
     t = x-y; 
     z = foo(x,y);
      … ; 
     return t; 
}
という場合には、関数gooでセットしたtの値が関数fooで壊される(これ が副作用!)ので、思いがけないエラーがおきてしまうことになります。 これが大 きなプログラムになると間違いを見つけるのが大変になってしまいます。局所 的しか使わない一時的なデータにはできるだけ局所変数を使い、必要なときだ け大域変数を使うようにしましょう。

関数が終わっても値を保持したい場合につかう静的変数は他の関数には使わ れたくないが、値を保持したい場合につかいます。(静的変数とはstaticを 宣言につけた変数)