数学的な意味での関数とは別に,C言語ではプログラムの中でひとまとまりの処理部分だけを「関数」と呼ぶ.他の言語ではサブルーチンとも呼ばれる.
C言語の関数には,自分で作成した関数と,あらかじめシステムが用意したライブラリ関数がある.ライブラリ関数のうち,どのコンパイラでも使用できる標準的なものを標準ライブラリ関数と呼ぶ.
説明に使用する用語をあらかじめまとめておく.
関数呼び出し | かんすうよびだし | 関数を実行すること |
関数名 | かんすうめい | 関数につけられた名前のこと.なるべく中身をわかりやすく表現すること |
実引数 | じつひきすう | 関数を呼び出すときに関数へ実際に渡すデータのこと |
仮引数 | かりひきすう | 関数の中身を書くときに,受け取る実引数を格納しておく予定の変数のこと. |
返値 | かえりち | 関数の結果の値のこと |
局所変数 | きょくしょへんすう | 関数の内部で宣言された変数のこと.その関数内部でしか通用しない. |
大域変数 (外部変数) | たいいきへんすう | どの関数の内側でもなく,外側で宣言された変数のこと.どの関数の中でも通用する. |
関数を使った実際のプログラム全体の例は次のようになる.
#include "sfr62a.h" /* 関数のプロトタイプ */ void main(void); int square( int ); //[1]関数squareのプロトタイプ.返値はintで,引数がintの関数だと分かる. void main(void) { ...mainの中身... x = square(3); //[2]関数squareの呼出 ...mainの中身続き... } int square(int param) //[3]関数square本体,中身をここに書く { int value; value = param * param; return value; }
プログラムファイルの最初の方に,使用するすべての関数プロトタイプを記述する.[1]関数プロトタイプとはどのような関数かを説明するための宣言である.そしてmainの中などで,その関数を[2]呼び出して使用する.ファイルの最後に,使用した関数の本体を記述する.[3]関数の本体とは,その関数の中身のことである.
この例では,ある数の二乗を計算する関数として名前が「square」という関数を考えている.まず,C言語では使用する関数の一覧を[1]プロトタイプとして宣言する必要がある.
int square(int);
とりあえずここでは,squareという名前の関数があることを理解しよう.細かいところは後回しにする.そして[2]関数呼出を先に説明しよう.このsquareを利用して「3」の二乗を計算するには,次のように書く.
int x; x = square(3); //[2] square関数の呼出し. ↑この例では「3」が実引数.squareに計算してもらうために渡すデータのこと ↑返値(squareが計算してくれた結果)がxに代入される.
関数名に括弧を付けて関数を利用する.これを関数を「呼び出す」と表現する.括弧の中の「3」は,実際にどういう値の二乗を計算して欲しいのかを関数squareに伝えるためのものである.関数を呼び出す時に括弧の中に書くこのようなデータのことを,「実引数」と呼ぶ.
また,関数の計算結果のことを「返値」と呼ぶ.この例では変数xに返値として9が代入されるはずである.
自作の関数の場合には,あらかじめ関数の中身もプログラムとして用意しておく.しかし,先ほど説明した実引数を受け取ってからでないと,実際の計算処理が開始できない.先の例では「3」を受け取らないと,いったい何の二乗を計算すべきかわからない.かといって「3」だけを前提にプログラムを書くわけにもいかない.別のプログラムでは「square(5)」や「square(11)」などのように別の実引数で呼び出されることもあるためである.
そこで,[3] 関数の本体では括弧の中に「仮引数」を用意する.関数squareの場合には次のようになる.
int square(int param) ←paramは仮引数 { int value; value = param * param; ←仮引数を使って計算 return value; }
この例ではparamと書かれたものが仮引数である.仮引数とは,呼び出されたときの実引数を受け取るための変数のことである.関数が呼び出されたとき,この例では自動的にparamに3が代入されてから関数squareの実行が開始される.そこで,squareの中ではそのparamの二乗値を計算してその結果をreturnで返している.
実際に,次の行を実行したときの,square内部の動きを説明しよう.
x = square(3);
square内部の動作: param=3 : 仮引数paramに実引数「3」を代入 ↓ value = param * param; //valueに「3 * 3」( = 9 )を代入 return value; //valueを返す ↓ 返値=value : 返値に9を代入
青く示した部分が自動的に行われ,最後に返値に格納されていた9がxに代入される.
この動作をまとめると,引数としてひとつのint型の値を受け取って,その自乗を計算して,int型の値を結果として返す関数である.これを表現したのが[1] 関数プロトタイプである.もう一度squareの関数をプロトタイプを示す.
int square(int);
また,C言語の関数の中には,引数のないものや,返値を持たないものもある.それらは次のように書くことになる.
x = date(); //引数のない例 (現在の日付を返すような関数) 引数は必要ないため空の括弧でよい. setDate("2007-10-31"); //返値のない例 (日付を設定するような関数) 返値は必要ないため受け取る変数がない.
書き方のより詳細な部分は次で説明しよう.
関数プロトタイプは,次のような形式で記述する.返値と仮引数の型は,普通の変数と同じ型(intやdoubleなど)を指定する.括弧の中の仮引数が複数ある場合には,カンマで区切って並べて書く.並べる順序にも意味があり,引数がその順番で必要であることを定義する.
返値の型 関数名( 仮引数の型 );
仮引数が複数必要な場合,例えば四角形の面積を求める関数を書くような場合,辺の長さが縦と横の二つ必要である.複数の仮引数を持つ関数は次のように仮引数をカンマ(,)で区切って必要な数だけ並べる.指定する仮引数の名前は,括弧の中では重複してはいけない.
返値の型 関数名( 仮引数1の型 仮引数1の名前 ,仮引数2の型 仮引数2の名前)
関数プロトタイプとして宣言した関数を,mainの中などプログラム中で呼び出して使用する.数学的な関数と同様に,関数は次のように関数名に括弧()をつけて呼び出す.
結果を受け取る変数 = 関数名(実引数)
例えば,関数「funcA」を実引数「123」で呼び出すには次のように書く.
int main(void) { int x; x = funcA(123); }
実引数には,数値データだけでなく,変数や計算式を書くこともできる.例えば
int hoge; hoge = なにか計算式; x = funcA( hoge );
x = funcA( hoge * 2 + 10 );
x = funcA( funcB( -6.6 ) );
引数が複数ある場合には,単純に対応する実引数を並べて書けばよい.ただし,呼び出す関数が必要とする仮引数と同じ順番に同じ型のデータを並べないといけない.
x = funcB( 100, -60, 78.66);
仮引数が一つもない場合には,空括弧のまま呼び出せばよい.
x = funcC();
返値がない場合には,結果を受け取る必要がない.
funcD(180);
通常,ファイルの最後の方に関数本体を記述する.関数の本体とは,その関数のプログラムの中身を書くための場所となる.関数の本体の先頭は,関数プロトタイプとまったく同じように書く必要がある.ただし,仮引数の名前を追加し,「;」の代わりに「{」「}」の間に関数の中身を記述する.もしも関数プロトタイプと仮引数の型や順序が違うと,「function ○○○ is redifined」(関数○○○が定義され直しました)というエラーを引き起こすので注意すること.
関数の本体は次のように記述すること.
返値の型 関数名( 仮引数の型 仮引数名) { 仮引数を使って計算処理を書く return 返値; }
関数内部の処理は,通常のC言語と同じで,その関数内部だけで利用できる変数を宣言し,if文やfor文,計算式などの実行文を書くことができる.特に関数内部で宣言した変数は,「局所変数(ローカル変数)」と呼び,その関数内部だけで通用する.もしも他の関数や外部で同じ名前の変数が宣言されていても問題ない.それとは別個の変数として扱われる.つまり,この内部で宣言した変数に値を代入しても,それは外部の同じ名前の変数には一切影響せず,外部の変数の値は書き換わらない.
また,関数の中から別の関数を呼び出すこともできる.
仮引数が一つもない場合には,キーワード「void」と書くか,空括弧のままにしておく.
返値の型 関数名(void) { }
C言語では,複数の値を返すことはできない.ただし,後述の大域変数を使えば,それに似た動作は書ける.
返値がない場合には,返値の型としてvoidを指定する.
void 関数名(仮引数の型 仮引数名) { return; //returnの後ろに何も指定せず,何も値を返さない }
先ほど説明したように,ある関数の中で宣言された変数を局所変数と呼ぶ.局所変数はその関数の中でだけ通用する変数である.その関数の外から(別の関数から)は,参照することができない.別の関数の中で同じ名前の変数があったとしても,一切関係がない.
逆に,すべての関数の外側で宣言された変数は,大域変数と呼び,逆にどの関数からも使用することができる.つまり関数プロトタイプを宣言したような場所で宣言された変数のことである.
#include "sfr62a.h" /* 関数のプロトタイプ */ void main(void); int square( int ); /* 大域変数 */ int global=0; //大域変数はすべての関数の外側で定義する.普通はかなり最初の方に書く. void main(void) { ここで,変数globalを参照したり,書き換えたりできる. } int square(int param) { ここでも,変数globalを参照したり,書き換えたりできる. }
関数の返値は常に一つしか書くことができないが,複数の値を返したいときには,次のように大域変数を利用すればよい.
int global01; //大域変数の例 int global02; //大域変数の例 int func(void){ //計算処理 global01 = 一つめの計算結果; global02 = 二つめの計算結果; return 三つ目の計算結果; }
このように,大域変数に結果の値をglobal01とglobal02に書き込めば,呼びだした側でもglobal01とgloval02を参照することができるので,その結果を受け取ることができる.
標準ライブラリ関数ならば,自分で定義せず呼び出すだけで使用できる.ただし,その呼び出したライブラリ関数の引数や返値の型がコンパイラが判断できないと正確に機械語に翻訳できないため,その関数のプロトタイプ宣言が必要である.
標準ライブラリ関数のプロトタイプ宣言は,標準ライブラリ用のヘッダファイルの中に書かれており,自分で書かなくてもよい.例えば三角関数sinやcosはヘッダファイルmath.hにプロトタイプが記述されているので,次のように利用する.
#include <math.h> #define PI 3.1415 int main(void) { double x; x = sin(PI/2.0) + cos(-PI/2.0); ...省略... }
どのような標準ライブラリ関数の種類があるのか,その関数を使用するときにはどのヘッダファイルを読み込めばよいのかは,コンパイラ付属の説明書や一般的なC言語のテキストを参照にすること.
組込用のプログラムの場合,入出力(キーボードや画面出力など)に関する標準入出力関数は使用できない.そもそもキーボードやモニタが接続されていないためである.本実験では標準ライブラリ関数は使用しない.