C言語は関数を組み合わせてプログラムを組み立てます。いままで、main関数のみを扱ってきましたが、それ以外のオリジナルの関数を作成することにより、プログラムの幅が広がります。
C言語での関数は、特定の処理をまとめたものです。繰り返し行う処理などを関数にまとめておくと、プログラムの作成が楽になります。数学での関数と扱いは似ており、計算(処理)に必要な条件を与え、その結果を得る、という使い方をします。C言語では関数に与える値を引数(ひきすう)、結果を戻り値(返り値)といいます。
また、数学の関数と違い、引数や戻り値の無い関数を作成することもできます。
関数の作成には、関数の型、関数名、引数を指定する必要があります。戻り値はreturnで指定します。戻り値の指定は必須です。ただし、戻り値の存在しないvoid型関数はreturnで戻り値を指定することができません。(※returnそのものは使用できます。その場合、関数から処理を抜けます。)
関数の型 関数名(引数の型と引数, …){ 処理 ・ ・ ・ return 戻り値 }
例えば、引数を2つ持ちその和を返す関数は次のようになります。
int function(int a, int b){ return (a+b); }
aとbが引数で、戻り値がa+bとなります。この引数a,bは関数内でもこの変数名で関数内で使用できます。この関数を使った例を下に示します。
#include <stdio.h> int function(int a, int b){ return (a+b); } int main(){ int a; a = function(1, 2); printf("%d",a); return 0; }
プログラムはmain関数からスタートします。functionは2つの数の和を返す関数なので、a=1+2=3となり、画面上に3が表示されます。function内で使用しているaはmain内のaとは別物です。
functionはmainの前に書きましたが、C言語は使用する関数が使用する部分の前に出ていなければ、エラーとなります。そのため、関数「A」の中で関数「B」が使用されている場合は、「A」より前に「B」を書いていなければなりません。
ただし、関数の絡み合いが複雑な場合は単純に書く順序を決められないことがあります。それを解決するために、「関数のプロトタイプ宣言」をします。
関数のプロトタイプ宣言では、関数型、関数名、引数を書きます。このプロトタイプ宣言をすることによって、関数を記述する順番を自由にすることができます。(※プロトタイプ宣言では引数を省略する書き方もあります。)
#include <stdio.h> /* プロトタイプ宣言 */ int function(int a, int b); int main(){ int a; a = function(1, 2); printf("%d\n",a); return 0; } int function(int a, int b){ return (a+b); }
関数内で別の関数を呼び出すこともできます。
#include <stdio.h> /* プロトタイプ宣言 */ int function(int a, int b); int function2(int a, int b); int main(){ int a; a = function(1, 2); printf("%d\n",a); return 0; } int function(int a, int b){ int c; if(a>b){ function2(a, b); } else { function2(b, a); } return c; } int function2(int a, int b){ int c; c = a - b; return c; }
mainでfunctionを、functionでfunction2を呼び出しています。
関数の中で自分自身の関数を呼び出すことができます。
#include <stdio.h> /* プロトタイプ宣言 */ int function(int a); int main(){ int a; a = function(10); printf("%d\n",a); return 0; } int function(int a){ int c = 0; if(a > 0){ c += function(a - 1); } return c + a; }
function関数の中でfunction関数を呼び出しています。こんなのは情報処理の試験なんかによく出たりします。
注意しないといけないのは、次のような無限に再帰するようなプログラムはスタックオーバーフローというエラーになり、強制終了してしまいます。
#include <stdio.h> /* プロトタイプ宣言 */ int function(int a); int main(){ int a; a = function(10); printf("%d\n",a); return 0; } int function(int a){ int c = 0; c += function(a - 1); /* 無限にfunctionが呼ばれ、抜けることがない! */ return c + a; }
普通の変数を引数とした場合、その値を関数内部で変えても呼出し元には影響しません。
#include <stdio.h> /* プロトタイプ宣言 */ int function(int a); int main(){ int a; int b = 6; a = function(b); printf("main: a=%d b=%d\n",a,b); return 0; } int function(int a){ a += 10; printf("function: a=%d\n",a); return a; }
関数にbの値が入れられ、その値に10を足したものをfunction関数内で表示、さらにmain関数内で、function関数の戻り値と、bの値を表示するプログラムです。この結果は次のように表示されます。
function: a=16 main: a=16 b=6
main関数のbの値には変化がありません。これは、function関数に渡す際に、bの値がコピーされてfunction関数に渡されているので、function関数の引数の値に変化が加えられたとしても、もとの変数には影響しないのです。
ところが、ポインタを引数とした場合は話が変わります。ポインタ→アドレスなので、変数のアドレスが関数に渡されることになります。アドレスのコピーはできないので、関数内で引数の値に変化を加えた場合、呼出し元の変数にも影響が出ます。
#include <stdio.h> /* プロトタイプ宣言 */ int function(int *a); int main(){ int a; int b = 6; a = function(&b); printf("main: a=%d b=%d\n",a,b); return 0; } int function(int *a){ *a += 10; printf("function: *a=%d\n",*a); return a; }
結果は次のようになります。
function: *a=16 main: a=16 b=16
mainから渡されたbのアドレスに対し変更を行っているため、mainのbの値も変わります。