第8回(2011年12月7日)


今日の演習

今日は以下の内容で演習を行います.


今日の目標

関数の定義の仕方と使い方を習得する.


数学関数の使い方

C言語では下記プログラムのように、様々な数学関数を用いることができます.例えば以下のようなプログラムで、三角関数同士の計算や指数関数等の利用ができます。(1207-1.c として打ち込んでみてください。)

- - - - - - - - - - - - - - - -

#include <stdio.h>
#include <math.h>

main()
{
     float pi,a,b,x,y;

     pi = 3.1415926;
     a = 0.3;
     b = 0.4;

     x = sin(2.0*pi*(a+b));
     y = sin(2.0*pi*a)*cos(2.0*pi*b) + cos(2.0*pi*a)*sin(2.0*pi*b);

     printf("%f %f %f\n", x, y, exp(x+y));
}
- - - - - - - - - - - - - - - -

数学関数を用いる場合には、

#include <math.h> をプログラムの先頭付近に書き入れることと、コンパイル時に、ファイル名を 1207-1.c とすると、

cc 1207-1.c -o 1207-1 -lm

というふうに、最後に -lm を付け足す必要があります.

sin(), cos(), exp()の他に, tan(), log() などいろいろ用意されています。(次回以降、よく使う事になります.)


関数の定義と利用

これまで、標準関数、数学関数と呼ばれる関数(printf 関数や scanf 関数、sin() や exp())をいくつか使いました.C言語では、自分で関数を定義し利用することができます.例えば beki(a,b) = ab という関数を自分で定義して利用する場合、次のサンプルプログラムのようになります. 1207-2.c として打ち込んでください.(説明の為の行番号と、色は無視してください.)

- - - - - - - - - - - - - - - -

 1: #include <stdio.h>
 2: 
 3: int beki(int a, int b);
 4:
 5: main()
 6: {
 7:     int n, x, y;
 8:
 9:     x = 2;
10:     y = 3;
11:    
12:     n = beki(x, y);    /* n = xy */
13:     printf("%d %d\n", n, beki(3,2));
14: }
15:
16: int beki(int a, int b) /* 実際に x = a, y = b として ab を求める (関数の定義) */
17: {
18:     int i, a_b;
19:
20:     a_b = 1;
21:
22:     for (i = 1; i <= b; i++)
23:     {
24:         a_b = a_b*a;
25:     }
26:  
27:     return a_b;
28: }

- - - - - - - - - - - - - - - -

青字の部分(3行目および16〜28行目)が関数の定義に関わる部分で、赤字の部分(12行目および13行目)で、定義した関数を使用している部分です.まずは定義している部分から見ていきましょう.

3行目: 関数のプロトタイプ宣言と呼ばれる宣言です.後に定義する関数の戻り値の型と、引数の型を仕様として、プログラムの先頭部分(main 関数より前)にこのように書きます.関数を定義する場合には、このプロトタイプ宣言が必ず必要だと思っておいてください.

5〜14行目: main 関数の定義です.

16〜28行目: beki 関数の定義です.


戻り値引数

例えば

y = sin(x)

と書いたとき、sin が関数名で x が関数 sin の引数変数 y には sin(x) の戻り値が入ることになります.当然変数 x, y にはそれぞれ型があり、関数自体がもつ値(戻り値)にも型があります.

 先のサンプルプログラムの3行目のプロトタイプ宣言や、16行目の関数定義の部分で、 int と整数型が宣言されているのはその為です.


beki 関数の定義

 では、beki 関数の定義部分を詳しく見ましょう.16行目を見ると

int beki (int a, int b)

となっています.これは、先のプロトタイプ宣言と通常おなじ記述となります.はじめの int は、 beki 関数の戻り値の型が int であるという意味です.続いて関数名 beki があり、その後括弧に囲まれ、2つの変数が定義されています.これらは、引数として渡される値を格納する為の変数でそれらの名前をそれぞれ a, b とし、型はそれぞれ整数型としています.つまり、 beki(1,2) とこの関数が呼ばれた場合、 a には 1 が、 b には 2 が代入されることになります.

 つづいて、中括弧に囲まれた、関数本体の処理内容が続きます.この中で、新たに(この関数の定義内でのみ有効な)変数 i と a_b を用意しています.さらには、先の引数部分にあった、変数 a, b が参照されています.もちろんa, b には、関数が呼ばれた時に引数として渡されている変数の内容である数値、または直接数値として渡された場合にはその数値が代入されています.

 最後の return 文により、 a_b の内容が beki 関数の戻り値として返され、関数が終了します.

以上、関数を作る場合に必要なことをまとめると

  • 関数の名前は何にするか
  • 関数に渡すデータの型と名前は何であって、数はいくつか(引数の数)
  • 関数から戻るデータ(戻り値)の型は何か
  • 関数のプロトタイプ宣言の記述(プログラムの先頭部分.main 関数の定義より前)
  • 関数本体の処理方法の記述
  • 結果を戻し、関数を終了するための記述(return 文)

ab を表示したいだけなら「関数の定義」等をするのはむしろ作業をややこしくしているだけに見えます。しかし、例えば自然数 x, y, z に対し xy, yz, zx, xy+z を表示したい場合、関数を使わないと少々見難いプログラムになりますが、beki関数を定義して使うと、以下のようにすっきりとしたプログラムになります。(このサンプルでは x=2, y=3, z=4 としています.)

- - - - - - - - - - - - - - - -

 1: #include <stdio.h>
 2: 
 3: int beki(int a, int b);
 4:
 5: main()
 6: {
 7:     int x, y, z;
 8:
 9:     x = 2;
10:     y = 3;
11:     z = 4;
12:    
13:     printf("%d %d %d %d\n", beki(x,y), beki(y,z), beki(z,x), beki(x,y+z));
14: }
15:
16: int beki(int a, int b)
17: {
18:     int i, a_b;
19:
20:     a_b = 1;
21:
22:     for (i = 1; i <= b; i++)
23:     {
24:         a_b = a_b*a;
25:     }
26:  
27:     return a_b;
28: }

- - - - - - - - - - - - - - - -

このように、繰り返し使用する演算(手続き)は、関数として定義しておくと便利です.


他の例を見てみましょう.

例えば、実数値の絶対値を表示するプログラムを考えましょう.これまでの知識からプログラムは大体次のようなものになります.

- - - - - - - - - - - - - - - -

#include <stdio.h>

main()
{
    float a;

    printf("実数を入力してください:");
    scanf("%f", &a);

    if (a < 0.0)
    {
        a = -a;
    }

    printf("入力された実数の絶対値は %f です.\n", a);
}

- - - - - - - - - - - - - - - -

このプログラムでは、 if 文を使って絶対値を求めていますが、絶対値を求める作業が多量にある場合、いちいち if 文を書くのは面倒です.絶対値を求める関数を作ってみましょう.(1207-3.c)

- - - - - - - - - - - - - - - -

#include <stdio.h>

/** 絶対値を計算する関数 float myabs(float f) の宣言 **/
float myabs(float f);

main()
{
   float a;

    printf("実数を入力してください:");
    scanf("%f", &a);

    printf("入力された実数の絶対値は %f です.\n", myabs(a));
}

/** 絶対値を計算する関数 float myabs(float f) **/
float myabs(float f)
{
    /* f に代入されている値がが負なら, 符号を変える */
    if(f < 0.0)
    {
        f = -f;
    }

    /* 計算結果を呼び出された場所へ戻す */
    return f;
}

- - - - - - - - - - - - - - - -

 このように自分で関数を作る場合には、その関数に自由な名前をつけることができます.しかし、標準関数、数学関数として既にある関数の名前をつけることはできません(例えば、printf や scanf といった関数を新たにつくることはできません).実は絶対値を求める標準関数 abs がありますので、今回は myabs という名前にしています.また、関数の名前も変数の名前の規則同様使える文字と使えない文字があります.注意してください.数字から始まらない英数字を関数名としてください.


課題1

上のプログラムを改造して、A[0] 〜 A[9] の10個の実数を入力すると、それぞれの絶対値を表示するプログラムを作成せよ.


課題2

上のプログラムを改造して、A[0] 〜 A[9] の10個の実数を入力すると、その和の絶対値と、それぞれの絶対値の和を表示するプログラムを作成せよ.


課題3

次の仕様を満たす関数を作成し、以下の問いに対するプログラムを作成せよ.

関数名: float sa(float a, float b)

仕様: 実数型の2つの数 a, b を受け取り、それらの差の絶対値を戻り値として返す.

問い:3つの実数 x, y, z を入力すると |x-y| + |y-z| + |z-x| を求めて表示する(発展問題:|sin(x-y)| - sin(|x-y|) を求めて表示する.)


課題4

次の仕様を満たす関数を作成し、以下の問いに対するプログラムを作成せよ.

関数名:int cut_off(int n)

仕様: 整数型の数 n を受け取り、その一の位を切り捨てした値を戻り値として返す

問い:5つの自然数 a[0] 〜 a[4] を入力すると、その和の一の位を切り捨てした値と、それぞれの一の位を切り捨てした値の和を表示する.(剰余を計算する演算子( % )を有効活用.)


課題5

サンプルプログラムや課題3を参考に, 3つの整数 x, y, z を入力すると (x-y)|x-y| + (y-z)|y-z| + (z-x)|z-x| を表示するプログラムを作成せよ.(関数は必要なだけ定義, 使用する事ができます.)


課題6

次の仕様を満たす関数を作成し、以下の問いに対するプログラムを作成せよ.

関数名:int round_off(int n)

仕様: 整数型の数を受け取り、その一の位を四捨五入した値を戻り値として返す

問い:7つの自然数 a[0] 〜 a[6] を入力すると、その和の一の位を四捨五入した値と、それぞれの一の位を四捨五入した値の和を表示する.


課題7

次の仕様を満たす関数を作成し、以下の問いに対するプログラムを作成せよ.

関数名:int sosu_hantei(int n)

仕様: 整数型の数 n を受け取り、n が素数なら 1 を, 素数でないなら 0 を戻り値として返す

問い:7つの自然数 a[0] 〜 a[6] を入力すると、入力された数の中に幾つ素数があったか表示するプログラム.


今日学んだ事

  • 関数を作成し、使うことができるようになりました

戻る