関数
C言語勉強会 第五回
kumar
May 14, 2013
引用 : Programming Place Plus
C言語勉強会 第五回
kumar
May 14, 2013
引用 : Programming Place Plus
これまでに、main関数、printf関数、scanf関数といった「 関数 」を使ってきた。本稿では、関数とは何か、それの使いかた、作り方について紹介する。
いきなりプログラムの関数の話をすると訳がわからないかもしれない
実際のところ 数学の関数とプログラムの関数は変わらない ので 数学の話からはじめる
数学でよくこんなのを見かける
数学の関数の例)
f(x) = 2x + 5
これは、とある数 x を”渡す”とxの2倍に5を足した 値を導く関数である。
この ”渡す”数、つまり()の中の数を、引数(ひきすう)と呼ぶ。
引数として2を渡せば、 f(2) は9という値を示す。
引数を”入力”、関数が示す値を”出力”と考えれば、関数とは、
「とある入力に対してとある出力を返すもの」
とも言える

関数は入力に対して出力を返すブラックボックス
出力は2つ以上になりえないが、入力、すなわち引数は複数になることもある
f(x,y,z,....) のように定義すれば 複数の引数を取ってもおk
ここからプログラムの話しだが、もう説明は終わったようなもの
プログラムの関数も数学の関数と変わらないことをしている
数学での f(x) = 2x+5 とは、
f という名前の関数は引数xをとり、
2x+5 という計算結果を返す関数である
と、定義されていることを意味する
さっきから返す値とかなんとか言ってるけど、コレを関数の 返り値 と呼ぶ
プログラムの場合は 値には型(intとかfloatとか)があるので、
を定義に含める必要がある
double function(double x) {
double answer;
answer = 2.0 * x + 5.0;
return answer;
}
double function の部分で、関数の名前が function であること、返り値の型が double 型であることを定義している(double x) の部分では、引数の名前がxであること、その型が double 型であることを定義している引数 xと、宣言した変数 answer を 使って計算し、
return answer;
により、変数answerの中身(今は計算し終わった値が入っている)を返した。
関数の便利なところの一つでもあるが、一度定義したら何度でも、どんな形でも自由に呼び出せる
double a = function(30);
のように関数の返り値を変数に格納(代入)してもいいし
if( function(4) >= 50 )
条件式の一部にしてみたり、計算式の一部にしてもいいし
int i;
for(i=0;i<10;i++)
function( i+i*2 );
変数や計算式を引数として渡してもいいし
printf("%lf", function(40.2) );
返り値を出力してもいい
引数を複数取るには定義部分で
int function( int x , double y , int zzz )
のように 引数と引数を ,( カンマ ) で区切って定義しよう
呼び出すときにも
function( 10 , 3.14 , 56 )
のように 区切って値を渡す
別に引数いらないよ!って関数ならわざわざ引数を定義することもない
int function( void )
と定義しよう
これで引数をとらない関数を定義できる
呼び出しは
function()
括弧内に何も書かずに呼び出せる
ここが数学の関数と少し違うところになるのだが、
数学の関数は「返り値を返すだけ」なのが普通なのに対して、
プログラムの関数は、返り値を返すこと以外に
「副作用を伴う」ことがままある
プログラムにおいては、返り値が不要で、副作用だけが必要な場合もある
※副作用:例えばprintf()による出力、exit()によるプログラムの終了等。
例えば、2つの引数の合計を出力する関数を考える
int sumPrint( int a , int b ){
printf("%d", a+b ); // 出力という「副作用」
return ?? //一体何を返す必要があるというのだろうか
}
そういう返り値を返さない関数は、「この関数には返り値が無いぞ」と、予め定義してしまおう。
int のかわりに void を付けて
void sumPrint( int a, int b ){
printf("%d", a+b );
}
とだけ定義すれば良い
返り値が無いので return は書かない
return が実行されるということは、 「その関数の終了」を意味することになる
if 文分岐などを使って複数の return を書いておけば、複数の「終わりのパターン」や、「場合に応じた返り値」を定義することが出来る。
ちなみに、返り値がある関数は return が実行されることで終わりを迎えるが void 型関数は関数の括弧閉じ } まで到達したところか、 return が実行されて関数終了となる
C言語では、必ず記述しなければならない必須の関数
ご存知の通り、C言語のプログラムは main関数から始まる。
int main(void) {
...
return 0;
}
戻り値の型が int型である以上、main関数には必ず return文がある。 main関数の戻り値を 0 にすると、問題無く正常に処理が終了したことを表し、 0以外を返すと、何らかのエラーが発生して終了したことを表すことになっています。
1度定義すれば何度でも~~~
とかいう説明をしたが、定義されていない関数は呼び出すことができない(当然か)
定義されていないのにいきなり
functioooon(13,23);
とか呼び出そうとしてもコンパイルエラーになってしまう コンパイラ「いや俺そんな関数知らねえしwwwwwwwwww」
さらに、変数の宣言と同じで、定義と呼び出しの順番は
変数宣言 → 変数使用 のように
関数定義 → 関数呼び出し という順番になっている必要がある
普段使っている scanf() 、 printf() も関数だが、どこかに 定義されているようには見えない
C言語の規格によって、あらかじめ用意されている関数
printf関数や scanf関数のようなものが標準ライブラリ関数に当たる。
標準ライブラリ関数を使うには、「#include <stdio.h>」とか書くことで関数の記述を読み込む。
「stdio.h」の部分は関数ごとに決まっている。
main関数はこのような記述が不要なので、標準ライブラリ関数とは別である。
C言語では、必ず記述しなければならない必須の関数
ご存知の通り、C言語のプログラムは main関数から始まる。
int main(void) {
...
return 0;
}
戻り値の型が int型である以上、main関数には必ず return文がある。 main関数の戻り値を 0 にすると、問題無く正常に処理が終了したことを表し、 0以外を返すと、何らかのエラーが発生して終了したことを表す(OSに伝える)ことになっている。
関数名・引数・戻り値の組み合わせを、関数本体と別に記述しておき、 このような関数が存在しているのだということを宣言するもの
double areaOfTriangle(double b, double h);/* 関数プロトタイプ */
#include <stdio.h>
double areaOfTriangle(double b, double h);/* 関数プロトタイプ */
int main(void) {
double a = areaOfTriangle(5.0, 8.0);
printf("底辺の長さ5.0、高さ8.0の三角形の面積は %f\n", a);
return 0;
}
double areaOfTriangle(double b, double h) {
double ans = b * h / 2.0;
return ans;
}
areaOfTriangleの関数プロトタイプをmain関数の上に書くことで、main関数の下にareaOfTriangleの定義を書くことが出来た
読まなくても分かりそうな話を色々おまけしておきます。
関数には、「関数名」「引数」「戻り値」という3つの構成要素があります。
関数は次のように定義します。
戻り値の型 関数名( 引数の型と名前, 引数の型と名前, … ) /* 引数は 0個以上 */
{
...
return /* 戻り値 */
/* 戻り値がなければreturn文自体が不要 */
}
この形式を見ると分かるように、引数や戻り値には型があります。また、引数には1つ1つに名前を付けます。 引数は0個以上の任意の個数記述できます。
0個の場合には、そのことを意味する「void」と書きます。
戻り値がない関数の場合は、戻り値の型は「void」です。
引数は、関数を呼び出す側と、呼び出される側とで、呼び名を変えることがあります。 呼び出す側は、引数を実引数と呼び、呼び出される側(定義の側)では仮引数と呼びます。
void printUnko(int num) {
int i;
for(i = 0; i < num; i++) {
printf("うんこ\n");
}
}
int main(void) {
printUnko(10);
return 0;
}
この例ではnumは仮引数、10は実引数
大規模なプログラムになると、自作のヘッダーファイル(〜.h)に関数プロトタイプだけ書いておいたりします。 例)ヘッダーファイル(triangle.h)
#ifndef TRIANGLE_H
#define TRIANGLE_H
double areaOfTriangle(double b, double h);
...
#endif /* TRIANGLE_H */
例)ヘッダーファイルの読み込み(main.c)
#include "triangle.h" /* triangle.hがソースと同じディレクトリにある場合 */