カテゴリー
C

C(Step4-1)

関数の利用①

「関数」という言葉は、ここまでの中で何度か出てきています。例えば、次のようなものです。

  • 結果を画面に表示する関数 printf()
  • データを入力する関数 scanf()
  • ファイルを開く関数 fopen()
  • ファイルに書き込む関数 fprint()

などの関数を使ってきましたが、ここでは、特に自分で関数を作ることを行っていきましょう。

C言語と関数

プログラムに新しい機能を加えるたび、C言語に標準で用意されている関数や、最初に#include<stdio.h>#include<string.h>と記述することで利用できるようになる関数を使ってきました。また、main()も関数にあたります。これは、プログラムを実行する時に一番初めに実行される部分という意味があります。

このように考えると、四則演算や括弧などの記号、変数宣言、制御文を除くと、C言語の記述は全て関数で成り立っています

自分で関数を作る

ここからは自分で実際に関数を作っていきましょう。関数を作るケースは、以下のような場合です。

  • 何度も使う記述は1回だけにまとめる
  • プログラム機能別に見やすくまとめる
  • 他のプログラムでも利用可能な資源を作る

このような関数を作る場面を次の例題で考えてみましょう。

例題:価格表の作成として、商品5個の原価を入力すると、以下の要素をまとめて表示するプログラムを作成してください。

  • 原価に30%を加えた原価(小数切り捨て)
  • 売価の10%の消費税(小数切り捨て)
  • 売価に消費税を加えた税込み価格

このプログラムを関数を使って記述したのが次のプログラムになります。

#include <stdio.h>

/*①配列の初期化関数*/
int initial_array(int price[5][4]){
	int i, j;

	for(i=0; i<5; i++){
		for(j=0; j<4; j++){
			price[i][j] = 0;
		}
	}

	return(0);
}

/*②商品5個の原価入力*/
int input_mod(int price[5][4]){
	int i;

	for(i=0; i<5; i++){
		printf("%d個目の商品の原価を入力してください\n", i+1);
		scanf("%d", &price[i][0]);

	}
	return(0);
}

/*③売価・消費税・税込み価格の計算*/
int calc_mod(int price[5][4]){
	int i;
	for(i=0; i<5; i++){
		price[i][1] = (int)(price[i][0] * 1.3);
		price[i][2] = (int)(price[i][1] * 0.1);
		price[i][3] = price[i][1] + price[i][2];
	}
	return(0);
}

/*④価格表の表示*/
int print_price(int price[5][4]){
	int i;

	printf("原価      :");
	for(i=0; i<5; i++){
		printf("%6d :", price[i][0]);
	}

	printf("\n売価      :");
	for(i=0; i<5; i++){
		printf("%6d :", price[i][1]);
	}

	printf("\n消費税    :");
	for(i=0; i<5; i++){
		printf("%6d :", price[i][2]);
	}

	printf("\n税込み価格:");
	for(i=0; i<5; i++){
		printf("%6d :", price[i][3]);
	}
	printf("\n");

	return(0);
}

int main(){
	int price[5][4];

	/*初期化*/
	initial_array(price);

	/*原価入力*/
	input_mod(price);

	/*売価・消費税・税込み価格の計算*/
	calc_mod(price);

	/*価格表の表示*/
	print_price(price);

	return(0);
}
sample4-1.1実行結果

プログラムを見ると、main()関数内では、「初期化」「原価入力」「各種計算」「価格表の表示」というアルゴリズムを簡単にしたスッキリとした記述になっていると思います。それぞれ、処理に対応する関数が記述されているからです。

関数の定義部で配列宣言をする

以前のプログラムとの違いは次の部分です。

int 関数名(配列宣言)  //関数の定義
{
       :
   return(0);
}

int main()
{
   配列宣言
     :
  関数名(配列名)  //関数の呼び出し
       :
   return(0);
}

ここでは、main()の中で、「関数名(配列名)」として関数を呼び出し、関数の定義では、「int 関数名(配列宣言)」と記述しています。配列を一緒に記述すると、関数の呼び出しで使っている配列が、関数の中でそのまま利用できます

①②③④の関数内部について

プログラム内の4つの関数は、内部でそれぞれ、i や j といった変数の宣言をしています。それぞれの関数は独立しているので、ある関数で使用していた変数を別の関数でも使いたいときは、再び宣言しないと使用できないです。そのため、それぞれの関数で、他の関数と値の関連性がない、その関数内だけで必要な関数を宣言しなくてはいけません。4つの関数で、i という変数を使っていますが、同一の名前でも全く別物として扱われます。

様々な関数を作ってみよう

「どのように関数を記述すればよいのか?」という部分でイメージがまだできないかと思います。ここでは、小さなプログラムを例として、そのプログラムの一部を関数に書き換える方法を説明していきます。

変数を渡さない関数

次のプログラムの一部を関数function1に書き換え、同じ機能のプログラムにするにはどうすれば良いか考えます。

#include <stdio.h>

int main(){
    printf("##################\n");
    printf("##Sample Program##\n");
    printf("##################\n");

    return(0);
}

上のプログラムは変数を何も使ってない記述です。この場合は、次の方法が簡単に関数に置き換えれます。

#include <stdio.h>

int function1(){
    printf("##################\n");
    printf("##Sample Program##\n");
    printf("##################\n");

    return(0);
}

int main(){
    function1(); //定義した関数を呼び出す

    return(0);
}

このような種類の関数は「タイトルの表示」など、特定のメッセージの表示などに利用すると良いです。

変数を渡すが、変数の値は変更しない関数

次のプログラムの一部を関数function2に書き換え、同じ機能のプログラムにするにはどうすれば良いか考えます。

#include <stdio.h>

int main(){
    int a,b,c;
    a = 10; b = 20; c = 40;

    printf("a + b + c = %d\n", a+b+c);
    printf("c - a - b = %d\n", c-a-b);

    return(0);
}

ここでは、変数の値を初期値のまま変更していません。この場合には、次の方法で関数に置き換えられます。

#include <stdio.h>

int function2(int a, int b, int x){
    printf("a + b + c = %d\n", a+b+x);
    printf("c - a - b = %d\n", x-a-b);

    return(0);
}

int main(){
    int a,b,c;
    a = 10; b = 20; c = 40;

    function2(a, b, c);

    return(0);
}

変数の値を参照する関数を作るときは、

  • 関数の呼び出し部では、関数名(変数名*, 変数名, …)
  • 関数の定義部では、int 関数名(変数宣言, 変数宣言, …)

という記述をします。ここで注意することは、呼び出し定義部では、変数の名前は同じものである必要はなく、変数を記述する順番だけが意味を持つということです。

変数を渡し、変数の値を変更する関数

次のプログラムの一部を関数function3に書き換え、同じ機能のプログラムにするにはどうすれば良いか考えます。

#include <stdio.h>

int main(){
    int a, b;
    a = 10; b = 20;

    a = a + 5;
    printf("a + b = %d\n", a+b);
    printf("a = %d\n", a);
    return(0);
}

最初に代入した変数の値を+10として変更しています。この場合この処理を関数にするには注意が必要です。なぜなら、「定義されている関数の中で変数の値を変えても、その関数の呼び出しを終了して呼び出した関数に戻ってきたときには、関数を呼び出す前の値に戻る」からです。

そこで、次のような記述にしなければいけません。

#include <stdio.h>

int function3(int *a, int b){
        *a = *a + 5;
        printf("a + b = %d\n", *a+b);

        return(0);
}

int main(){
    
    int a, b;
    a = 10; b = 20;

    function3(&a, b);
    printf("a = %d\n", a);
    return(0);
}

この例からわかるように「変数の値を変更する関数」では、変更する変数を渡すときには「&」を変数名の先頭につけ、呼び出された関数のほうでは、「*」をつけて宣言し、取り扱わなければなりません。このような操作をcall by referenceと言います。

配列を関数に渡して利用する

次のプログラムの一部を関数function4に書き換え、同じ機能のプログラムにするにはどうすれば良いか考えます。

#include <stdio.h>

int main(){
    int x[2];
    x[0] = 10; x[1] = 20;

    x[0] = x[0] + 10;
    printf("x[0] + x[1] = %d\n", x[0] + x[1]);

    printf("x[0] = %d\n", x[0]);
    return(0);
}

配列x[0]の要素の値を+10として変更しています。この処理を関数にするには注意が必要です。

#include <stdio.h>

int function4(int a[2]){
    a[0] = a[0] + 10;
    printf("x[0] + x[1] = %d\n", a[0] + a[1]);

    return(0);
}

int main(){
    int x[2];
    x[0] = 10; x[1] = 20;
    function4(x);

    printf("x[0] = %d\n", x[0]);
    return(0);
}

ここでは、「配列の値を変更する関数」を記述するときに使った「&」や「*」を利用していません。しかし、ちゃんと関数内では更新した値が反映されます。配列の場合は、配列名を渡して関数を呼び出すだけで、その関数の中で更新した値が呼び出した関数側でも反映されます。