数学の関数とはちょっと違うよ。

関数

C言語でいう関数とは、「 何かをするためにしなければならない一連の処理をまとめて、名前をつけたもの 」の事を言います。C言語のプログラムは、この「関数の集まり」でできています。 そして関数には、「予め用意されている関数」と、「自分で作る関数」があります。C言語のプログラミングでは、予め用意されている関数と自分で作った関数を組み合わせて、目的のプログラムを完成させるのです。

いままで、様々なC言語のコードを書いたり見たりしてきましたが、画面に文字列を出力する時は以下のような文がありました。

printf("こんにちは。\n");

この printf とは、一体何のことでしょうか?

画面に文字や変数の値を出力させるには、本来は様々な処理をしなければなりませんが、このprintf()関数があることによって、たった1行で画面への出力がなされているのです。

前に、「おまじないの#include <stdio.h> を無くしたらどうなるか?」という話をしたと思いますが、このおまじないをなくすとprintf()関数やscanf()関数が使えなくなります。(jocalcだとコンパイラがいい感じにやってくれてエラーが出なかった気もするけど)

ということは、printf()関数やscanf()関数の中身(実装といいます)は、どこかにある stdio.h というファイルの中に定義されている、という想像ができますね。拡張子の h は、ヘッダファイルという事を表しています。ヘッダファイルには、printf()やscanf()などの関数の中身が定義されています。

つまり、#include <stdio.h> というのは、「printf()関数やscanf()関数が定義されている、stdio.hというファイルをインクルード(≒ 読み込む)しますよ」という意味なのでした。

このような、特に何もしなくても必要なヘッダファイルさえインクルードしてしまえば使える関数の事を標準ライブラリ関数といいます。

標準ライブラリ関数

今まで使ってきた標準ライブラリ関数は、printf()とscanf()くらいでしょうか。これらはstdio.hに定義されている、入出力関数といいます。Standard Input/Outputの略で、stdioです。今までは標準出力と標準入力に対する処理を行う関数のみを扱ってきましたが、ファイルの読み書きも可能です。

他にも様々な分野の標準ライブラリ関数が用意されていますが、よく使うものには、次のようなものがあります。

  • 汎用関数 stdlib.h
  • 文字列処理関数 string.h
  • 数学関数 math.h
  • 時間関数 time.h

自分で関数を作ってみる

予め用意されている関数を「標準ライブラリ関数」と言いましたが、自分で作る関数の事は「 ユーザ関数 」だとか「 自作関数 」といいます。

まずは、簡単な関数を作ってみましょう。以下のコードを記述してください。ファイル名は、func.cとします。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#include <stdio.h>

int sum(int x, int y)
{
        return x + y;
}

int main(void)
{
        int result;

        result = sum(10, 5);

        printf("sum(10, 5)の結果は、%dです。\n", result);

        return 0;
}

ユーザ関数(以降、単に関数と呼びます。)を作る場合は変数と同様に、宣言する必要があります。関数の宣言は、以下のように記述します。なお引数はひとつでも、2つ以上でも構いません。

返り値の型 関数名(引数1の型 引数1の名前, 引数2の型 引数2の名前 )

int型の引数xとyを受けとり、その合計をint型の値として返す関数sumを作ったので、宣言は以下のとおりでした。

int sum(int x, int y)
{
    return x + y;
}

そうして定義したsum()関数を、main()関数内で呼び出しましたね。

int main(void)
{
        int result;
        result = sum(10, 5);

        printf("sum(10, 5)の結果は、%dです。\n", result);
        return 0;
}

ここで、 呼び出し 引数 返り値 という新しい言葉が出てきました。

まず、 呼び出し について。関数は宣言しただけでは意味がありません。関数を使うことを、「関数を呼び出す」といいます。

次に 引数(ひきすう) とは、関数を呼び出す際に与える値のことです。 int sum(int x, int y)と宣言すると、その関数の中でだけxとyという変数が使えるようになります。変数xとyには、関数を呼び出す時に与えられた値(この場合、xには10が、yには5)が代入されています。

そして 返り値(かえりち) とは、関数が返す値のことです。sum()関数の場合は、引数として受け取った2つの変数の値を足し合わせて、それを返していました。関数が値を返すには、return文を使います。

return x + y;

関数は、呼び出される際に引数を受け取り、何らかの処理をした後に、返り値を返します。なお、引数や返り値は必ず無くてはいけないというものではなく、必要ない場合は省略することも出来ます。なお、返り値を省略する時は、voidを使います。

引数や返り値を省略した関数を作って、それを使ったプログラムを書いてみましょう。ファイル名は、sayhello_func.cとします。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#include <stdio.h>

// Hello! と決まりきった文字列を出力するだけなので引数も返り値もいらない。
void sayHello()
{
        printf("Hello!\n");
}

// 受け取った文字列を出力する
void say(char message[])
{
        printf("%s\n", message);
}

int main(void)
{
        sayHello();
        say("My name is Takuya");

        return 0;
}

関数のスコープとローカル変数、グローバル変数について

ローカル変数や関数のスコープについて軽く触れておきましょう。以下のコードを書いてみてください。ファイル名は、local_var.cとします。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
#include <stdio.h>

void func(int x)
{
        int y,a = 3;

        printf("func()関数にて、引数としてx = %dを受け取りました。\n", x);

        printf("xを2乗します。\n");

        x = x * x;

        printf("func()関数内でのxは、%dになりました。\n", x);

        printf("y = a * xを計算します。");
        y = a * x;
        printf("yは、%dになりました。\n", y);
}

int main(void)
{
        int x = 10;
        printf("main()関数内でのxは、%dです。\n", x);

        func(x);

        printf("main()関数のxは、%dのままです。\n", x);

        return 0;
}

関数内で宣言された変数を、ローカル変数といいます。

変数にはスコープがあるため、関数が違うと同じxという変数名の変数でも、違うものとして扱われます。 今回の例だと、変数xは、main()関数と、func()関数にありますが、それぞれ違うものとして扱われています。

また、変数にはグローバル変数というものもあります。次のコードを書いてみてください。ファイル名は、global_var.cとします。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
#include <stdio.h>

int sum;

void func(int x)
{
        sum = sum + x;
}

void func2(int x2)
{
        sum = sum + x2;
}

int main(void)
{
        sum = 0;

        printf("初期状態: ");
        printf("sum = %d\n", sum);

        func(10);
        printf("func()実行後: ");
        printf("sum = %d\n", sum);

        func2(200);
        printf("func2()実行後: ");
        printf("sum = %d\n", sum);
}

関数の外で変数を宣言すると、それはグローバル変数になり、すべての関数内で変数が共有されます。一見便利そうですが、誤ってデータを破壊してしまう可能性などがあるので、使わないほうが良いとされています。

変数のスコープとローカル変数、グローバル変数については、頭の片隅に入れておいてください。

関数のプロトタイプ宣言について

あらかじめ先頭で関数の形を宣言しておくことで、他の全ての関数からその関数を使えるようにすることをプロトタイプ宣言と呼んでいます。プロトタイプ宣言さえしておけば、関数をどんな順序で書いても関係ありません。

どんな関数が作られているのか、前もって一覧にしておくようにすれば、いちいち関数を前に書いたり後に書いたりする心配はなくなります。関数の一覧を作るには、関数の形を、プログラムの初めの方に並べます。関数の形とは、関数の1行目の後ろに ; をつけたもののことです。正確には、関数名、関数の型、引数の型、の3つを書いておくことです。

次のプログラムは、1から100までの和を求めるsum関数の概要をあらかじめ書いておいた例です。写さなくてもok。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#include <stdio.h>

int sum(void);	/* プロトタイプ宣言 */

int main(void)
{
        sum();
	return 0;
}

int sum(void)
{
	printf("%d\n",(1 + 100) * 100 / 2);
	return 0;
}

練習問題

y = f(x)的な

プログラミングの関数と数学の関数は違いますが、数学の関数っぽい使い方もできます。

関数f(x) = 3x をC言語の関数で実装してみましょう。ファイル名は12_ren1.cとし、main()関数は以下のとおりとします。また、f(x) = 3xの3(つまり、傾き)はaとして関数f()内で定義し使ってください。

int main(void)
{
        int x;
        int y;

        printf("整数値xを入力\n");
        printf("--> ");

        scanf("%d", &x);

        y = f(x);

        printf("y = %d\n", y);

        return 0;
}

yourName()関数を作る

文字列を受け取って、「君の名は、○○」と表示する関数yourName()を作りましょう。ファイル名は12_ren2.cとし、main()関数は以下の通りとします。

int main(void)
{
    yourName("新海誠");
    // "君の名は、新海誠" と表示される。

    return 0;
}

decorate()関数を作る

文字列を受け取って、飾り付けして表示する関数decorate()を作りましょう。ファイル名は12_ren3.cとし、main()関数は以下のとおりとします。

int main(void)
{
        decorate("NIT, Tomakomai College");
        // "-----| NIT, Tomakomai College |-----"と表示される

        return 0;
}

おまけ

自分で関数を作成して、プログラムを好きに作ってみよう!