情報基礎演習II − 第3回


1.関数


数学の関数は、例えば f(x) = x2 と定義されているとき、 f(2) = 4, f(3) = 9 になります。 すなわち、 パラメータとして与えた値を定義された式で計算した値と等しいもの として扱います。
したがって、g(x) = f(x) * x と言うように、 関数を別の関数定義で参照することもできます。
C言語においても、 関数はなんらかのデータを得て、 それを処理し、 なんらかの結果を返します。 これがC言語のプログラムの単位になります。 結果を返す関数は、 定数や変数同様、値を持つものとして扱われます。 数学の関数と違うところは、 その定義が「手続き」であるところです。

f(x) や g(x) をC言語の関数として表せば、 次のようになります。

int f(int x)
{
    return x * x;
}

int g(int x)
{
    return f(x) * x;
}
関数 g(x) は関数 f(x) を評価してその値を求め、 それに x をかけたものを自分自身の値としています。

main も一つの関数です。

int main(void)
{
    return 0;
}
この例は関数 main を定義し、 { } 内に main において行うべき処理を記述しています。 return 0; によって、 この関数の値を 0 に設定し、 この関数を評価しているところに戻ります。 この関数の値のことを関数の戻り値と言います。 main の左の int は、この戻り値(すなわち関数自身)のデータ型を示します。

プログラムを実行すると、 オペレーティングシステムは 最初にこの関数 main を評価します。 上の例では関数 main を評価した値は 0 になります。 オペレーティングシステムはこれを 終了ステータスに設定して、 プログラムを終了します。

この main のような名前を関数名といいます。 関数名の付け方は 変数名と同じです。 ただし main は、上で述べたように オペレーティングシステムがプログラムの実行を開始するときの 「目印」として使いますから、 プログラム中には main が必ず1つだけ存在する必要があります。

■課題7■


ライブラリ関数

main から別の関数を呼び出すことができます。

#include <stdio.h>

int main(void)
{
    printf("Hello!\n");
    return 0;
}

この例では、 main から printf という関数を呼び出し、 それに "Hello!\n" というデータを与えています。 このように関数に与えるデータのことを引数(ひきすう)と呼びます。 printf は、引数に与えられたデータを標準出力に出力(画面に表示)します。

画面表示(入出力)のようにプログラム中で頻繁に使われる機能は、 あらかじめ部品のように使える関数として用意されています。 このような関数をライブラリ関数と言います。 printf はライブラリ関数のひとつです。 これはリンクのときに ライブラリファイルから取り出されて、 main 関数と結合されます。

このプログラムの1行目にある、

#include <stdio.h>
は、stdio.h という別の場所(/usr/include)にあるファイルを ソースファイルのこの位置に埋め込みます。 このファイルには printf のような標準入出力(standard input/output)を使う ライブラリ関数の宣言などが記述されています。 printf を使うときは、 おまじないだとでも思って、 プログラムの先頭にこの1行を入れてください。

printf は複数の引数を受け取る場合もあります。 次の例は、 printf に ,(コンマ)で区切って3つの引数を与えています。

printf("%dの2倍は%d\n", x, x * 2);
このうち一つ目の引数 "%dの2倍は%d\n" は書式文字列と呼ばれ、 何をどのような書式で出力するのかを printf に指示します。 書式文字列の最後の \n は改行を示します。

書式文字列に含まれる2つの %d は変換指定子と呼ばれ、 その位置にそれぞれ二つ目、三つ目の引数として与えられた式(整数型)を、 その位置に10進数の文字列に変換して (コンピュータの内部では2進数だから)表示することを示しています。

書式文字列中で % 自身を表示したい場合は、%% と書きます。

printf("%dの10%%は%dです\n", x, x * 10 / 100);

■課題8■


関数を定義する

自分でも関数を作ることができます。 自分で関数を作ることで、 プログラムを複数の部分に分割することができます。

#include <stdio.h>

void sub1(void)
{
    printf("関数sub1を実行しています\n");
}

void sub2(void)
{
    printf("関数sub2を実行しています\n");
}

int main(void)
{
    printf("関数mainを実行しています\n");
    sub1();
    sub2();
    printf("関数mainが終了しました\n");
    return 0;
}
この例では、 main の中から sub1 と sub2 を順に呼び出しています。

■課題9■


引数

関数にデータを渡すには、引数を用います。

#include <stdio.h>

void add(int a, int b) /* 仮引数の宣言 */
{
    printf("%d+%d=%d\n", a, b, a+b);
}

int main(void)
{
    int i, j;

    i = 10;
    j = 20;
    add(i, j);      /* i, j は実引数 */
    return 0;
}
関数が引数を受けとるときは、 関数名(上では add)に続く ( 〜 ) 内に 引数を受け取るデータ型と変数名の対をコンマで区切って列挙します。 この変数を仮引数と呼びます。

次のように、仮引数の並びが void のときは、 この関数が引数を使用しないことを表します。

void sub(void)
{
    ...
}
一方、関数を呼び出すときに実際に与えるデータを実引数と呼びます。 仮引数と実引数のデータ型は一致していなければなりません (実は関数が参照場所より前で定義されている場合や 関数プロトタイプが定義されている場合は、 実引数の型を仮引数の型に合うよう変換してくれる場合もあるんですが、 いろいろややこしいことも起こったりするのでとりあえずそう思っていてください)。

なお、古い形式のC言語では、 次のように関数名の宣言の次に、 引数の宣言を置きます。

add(a, b)   /* 仮引数の宣言     */
int a, b;   /* 仮引数の型の宣言 */
{
    printf("%d+%d=%d\n", a, b, a+b);
}
ここでは ANSI-C(新しい版のC)の形式を扱うことにします。

■課題10■


戻り値

関数からは、戻り値を使って 関数を呼び出した側にデータを返すことができます。

#include <stdio.h>

/* 引数に与えられた整数の和を返す関数の定義 */
int add(int a, int b)
{
    int c;

    c = a + b;
    return c;       /* 結果を返す、return a+b; でも可 */
}

/* メイン関数の定義 */
int main(void)
{
    int i, j, k;

    i = 10;
    j = 20;
    k = add(i, j); /* 関数 add の戻り値が k に代入されます */

    return 0;
}
return に続く式の値が、その関数の戻り値になります。
return a+b;
関数は戻り値によって呼び出した側に値を返すことができるので、 呼び出した側では関数を定数や変数同様、式の中で使用できます。 関数 add を次のように使うこともできます。
k = add(10, 20) * 3;  /* (10 + 20) * 3 */
main 戻り値はプログラムの終了ステータスになります。
return 0; /* 0 は正常終了を示す終了ステータス */
戻り値がある関数は、 戻り値のデータ型を関数自身の型として宣言しておく必要があります。
int add(int a, int b) /* add は int 型      */
{
    int c;
    ...
    return c;         /* c も int 型        */
}
型宣言が省略された関数は int 型だとみなされます。
main(void)            /* main は int 型     */
{
    ...
    return 0;         /* 0 は int 型 の定数 */
}
戻り値のない関数は、 そのことを明示するために void 型として宣言しておくべきです。
void sub(void)
{
    printf("Hello\n");
}
void sub(void)
{
    printf("Hello\n");
    return;
}
上の2つは同じです。 関数中に return が無ければ、 その関数の終りでこの関数を呼び出している所に戻ります。

return は関数の途中に書くこともできます。

int main(void)
{
    sub1();
    return 1; /* ここでプログラムが終わってしまう */
    sub2();
    return 0;
}
この場合、関数 sub2 は実行されません。

■課題11■