第5回:関数(2)

前回に引き続き関数を取り上げます.


前回のこたえ

【練習】

・長方形の二辺の長さを渡すと,面積を戻す関数を作成してみよう.

/* 長方形の二辺の長さを渡すと,面積を戻す関数 */

#include <stdio.h>

int area(int a, int b)
{
    return a * b;
}

void main()
{
    int a, b;
	
    printf("たて="); scanf("%d", &a);
    printf("よこ="); scanf("%d", &b);
	
    printf("面積=%d", area(a,b));
}
				

・円の半径を渡すと,面積を戻す関数を作成してみよう. 面積は,実数であることに注意.

/* 円の半径を渡すと,面積を戻す関数 */

#include <stdio.h>

float carea(int r)
{
    return r * r * 3.14;
}

void main()
{
    int r;
	
    printf("半径="); scanf("%d", &r);
	
    printf("面積=%f", carea(r));
}
				

【演習】

ある数 n の階乗 n! を求めるプログラムを,関数を用いて作成せよ

ただし,0! = 1 であることに注意.

/* ある数 n の階乗 n! を求めるプログラム */

#include <stdio.h>

float fact(int n)
{
    int i;
    float f = 1.0;

    for(i=0; i<n; i++){
        f *= (i + 1);
    }

    return f;
}

void main()
{
    int n;
	
    printf("n="); scanf("%d", &n);
	
    printf("n!=%.f", fact(n));
}
				

ここで, n! = n * (n-1)! であることを利用してプログラムを書くと,どうなるか?考えてみよう.

C言語では,関数の中から自分自身を呼び出すことができます.(これを再帰といいます)

どうなっているのか,わかるかな?

/* ある数 n の階乗 n! を求めるプログラム */
/* 再帰関数バージョン*/

#include <stdio.h>

float fact(float n)
{
    if(n == 0){
        return 1.0;
    }else{
        return n * fact(n-1);
    }
}

void main()
{
    int n;
	
    printf("n="); scanf("%d", &n);
	
    printf("n!=%.f", fact(n));
}
				

変数のスコープ

 変数は,宣言する場所によって,その有効範囲が異なります.同じ変数名なのに,関数によって値が違ってくるのは,このためです.

 ある関数の中で宣言した変数のことをローカル変数といいます.ローカル変数を利用できる範囲は,変数を宣言した関数の中に限られます.この変数の有効範囲のことを変数のスコープと呼びます.

int funca()
{
int y;

.....

return y;
}

変数yの有効範囲

変数yは,関数funcaの中のみ有効

void main()
{
int x;
x = 3;
y = 4; ×エラー!!
}
main関数では,変数yは使えません

 関数の外で宣言した変数のことをグローバル変数といいます.グローバル変数は,どの関数からも参照できます.

int y;
int funca()
{
....

return y;
}

変数yの有効範囲

変数yは,main関数の中でも使える

void main()
{
int x;
x = 3;
y = 4; ◎
}


値参照(Call by value)

これまでの関数では,mainから関数へ変数の値(中身)が渡されています.これを値参照(Call by value)といいます. 値参照では,関数を呼びだした元の変数は変化しません.元の関数に結果を返すにはreturn文を用います.このため,戻り値はひとつです.

簡単なプログラムで確認してみましょう.

#include <stdio.h>

int test(int a) 
{
    a += 10;

    return a;
}

void main()
{
    int a, b;

    printf("aの値を入力してください:");
    scanf("%d", &a);

    b = test(a);

    printf("aの値は%d\nbの値は%d\n", a, b);
}

aに2を入力すると,結果はどのように表示されるだろう.


これまでは,一つの値しか関数から戻すことができなかった.また,同じ変数名を使っていても,関数の方で値を変えても,mainでは変化しなかった.これは,関数には変数の中身が伝えられているためです.

ここで,関数から複数の値を戻したい場合や,文字列や配列データを渡したい場合を考えよう.この場合,関数に変数のしまってある場所を伝え,その場所のデータを関数で使うようにすれば,よいと考えられる.

変数のしまってある場所(アドレス)を扱うのは,ポインタの出番です.

アドレス参照(Call by reference)

ポインタを用いて,変数の場所を関数に渡したプログラムの例です.

#include <stdio.h>

int test2(int *a)        /* 変数aのアドレスを受け取る(ポインタ変数) */
{
    *a += 10;             /*aの指すアドレスの中身に1を加えるから,main関数でも変化する */

    return *a;
}

void main()
{
    int a, b;

    printf("aの値を入力してください:");
    scanf("%d", &a);

    printf("関数へ渡す前のaの値は,%dです\n", a);

    b = test2(&a);        /* 変数aのアドレスを関数に渡す */

    printf("関数から戻ると...\naの値は%d\nbの値は%d\n", a, b);
}

aに2を入力すると,どのように関数にわたり,

ポインタを用いて,アドレスを関数に渡せることがわかりました.

では,関数に配列データを渡してみましょう.

まずは,入力された文字の長さを返す関数を作ってみましょう.


配列の受け渡し

配列を受け渡している例を見てください.

#include <stdio.h>

void dim_init(int *ary, int no, int val)
{
    int i;

    for(i = 0; i < no; i++){
        ary[i] = val;
    }
}

void main()
{
    int i;
    int  ary[] = {1,2,3,4,5};

    dim_init(ary, 5, 0);

    for(i =0; i < 5; i++){
        printf("ary[%d] = %d\n", i, ary[i]);
    }
}

このプログラムは,配列aryの先頭からno番目までの要素の値をvalに設定する関数です.関数の初期化などの際に用います.関数には,配列の先頭のアドレス(ary)が渡されるので,関数側ではポインタ(int *ary)で受け取っています.関数で変更の加えられたデータは,特に戻り値を指定していませんが,main関数では,その変更が反映されます.


文字列の受け渡し

はじめに,文字列の扱い方を復習しよう.

文字列は,配列として扱うことを思い出してください.関数に文字列を受け渡すには,上記の配列の場合と同様,ポインタを用います.

ここで,文字の長さを数えるプログラムを関数を用いて書き換えてみよう.

#include <stdio.h>

void main()
{
    char str[128];
    int  i;

    printf("文字列を入力してください:");
    scanf("%s", str);

    for(i = 0; str[i] != 0x00; i++);

    printf("入力された文字列は,%sです.", str);
    printf("文字数は,%dです.",i);
}

ここで,青い色の部分を関数にしてみよう.

#include <stdio.h>

int moji_nagasa(char *str)
{
    int i;

    for(i = 0; str[i] != 0x00; i++);

    return(i);
}

void main()
{
    char str[128];
    int  i;

    printf("文字列を入力してください:");
    scanf("%s", str);
    printf("入力された文字列は,%sです.", str);
    printf("文字数は,%dです.", moji_nagasa(str));
}


演習

(1) 変数 a と b の値を,入れ替える関数を作成せよ.

(2) 配列の要素の和を求める関数を作成せよ.

(3) ある文字列を入力したとき,文字数を数える関数を利用して,文字を逆に並べる関数を作成せよ.