関数

C言語勉強会 第五回

kumar
May 14, 2013
引用 : Programming Place Plus

関数(サブルーチン)

これまでに、main関数、printf関数、scanf関数といった「 関数 」を使ってきた。本稿では、関数とは何か、それの使いかた、作り方について紹介する。

数学における関数

いきなりプログラムの関数の話をすると訳がわからないかもしれない

実際のところ 数学の関数とプログラムの関数は変わらない ので 数学の話からはじめる

数学の関数は引数を〜〜〜して値を導くもの

数学でよくこんなのを見かける
数学の関数の例)

f(x) = 2x + 5

これは、とある数 x を”渡す”とxの2倍に5を足した 値を導く関数である。

この ”渡す”数、つまり()の中の数を、引数(ひきすう)と呼ぶ。

引数として2を渡せば、 f(2)9という値を示す。

入力があると出力を返すのが関数

引数を”入力”関数が示す値を”出力”と考えれば、関数とは、
「とある入力に対してとある出力を返すもの」

とも言える

ブラックボックス(Wikimediaより)

関数は入力に対して出力を返すブラックボックス

引数は何個でも

出力は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;
}

値を返す

引数 xと、宣言した変数 answer を 使って計算し、

return answer;

により、変数answerの中身(今は計算し終わった値が入っている)を返した。

return文諸々

定義できたら呼びだそう

関数の便利なところの一つでもあるが、一度定義したら何度でも、どんな形でも自由に呼び出せる

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は複数個書いてもおk

return が実行されるということは、 「その関数の終了」を意味することになる

if 文分岐などを使って複数の return を書いておけば、複数の「終わりのパターン」や、「場合に応じた返り値」を定義することが出来る。

ちなみに、返り値がある関数は return が実行されることで終わりを迎えるが void 型関数は関数の括弧閉じ } まで到達したところか、 return が実行されて関数終了となる

main関数は特別な関数

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ライブラリ - Wikipedia

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つの構成要素-

関数には、「関数名」「引数」「戻り値」という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がソースと同じディレクトリにある場合 */

諸々