第10回 プログラミング入門 演習

【演習の注意事項】を良く読んで解答してください


関数(1)

【主な内容】

・プロトタイプ宣言
・関数の定義
・関数の使い方

演習課題

以下の3つのレベル(初級レベル, 中級レベル, 上級レベル)の全ての 課題に解答しなさい。各々の課題毎にファイルを作成しなさい。

課題はテキスト(文書)ファイルで提出するものと C言語のプログラムファイルで提出するものがあります。 前者は".txt"、後者は".c" という拡張子をつけて下さい。 各課題にはファイル名が指定してあるので、その指示にしたがって下さい。

なお、実行例やコマンドなどで赤色の字は キーボードから入力したもの、 深緑の字はコンピュータからの表示を示しています。 また、%はプロンプト(皆さんの環境では std1ss1{s1160000}1:等に相当する) を示しており、皆さんが入力するのはその次の文字からになります。

例題

講義資料(ハンドアウト)の例題 lec10-1.clec10-1b.clec10-2.clec10-3.clec10-4a.clec10-4b.clec10-4c.clec10-5.c  のソースプログラムをコピーし、そのプログラムの動作を予測した上で、コンパイル・実行を試してみなさい。

コピーの方法:
% cd ~/Prog0/Ex10
% cp /home/course/prog0/public_html/2008/lec/source/lec10-*.c .

初級レベル

(A-1)関数の利点
関数を使用することによる利点を記述しなさい。

ファイル名:ex10a1.txt


(A-2)関数の初歩
以下の3つの項目に対する解答を記述しなさい。

ファイル名:ex10a2.txt

1.引数と仮引数の違いを述べなさい。
2.local 変数とは何か、また利用方法を説明しなさい。
3.関数を含む標準的なプログラムの構成を述べなさい。


(A-3)関数の使い方の理解
次の2つのプログラムの下線部を埋めて完成させなさい。

ファイル名:ex10a3a.c, ex10a3b.c

(1)ファイル名: ex10a3a.c
#include <stdio.h>

____ triple( ____ );

main()
{
  float x = 2.0;

  printf("x:%f, x*x*x: %f\n", x, triple( x ) );
}

float triple( float x )
{
  return x*x*x;
}

(2) ファイル名: ex10a3b.c
#include <stdio.h>

double triple( ____ );

main()
{
  double t, x = 2.0;
  t = triple( x );
  printf("x:%f, x*x*x: %f\n", x, t );
}

double triple( ____ x )
{
  return x*x*x;
}


(A-4)関数の戻り値
以下のプログラムを実行し、関数 scanf() の戻り値を書きなさい。

ファイル名: ex10a4.txt

入力は以下の組み合せを試してみること。
3    5
2    4.6
3.2  6.7
5.2  8
3    x
x    5
#include <stdio.h>

main()
{
  int a, b, value;

  printf(" a, b を入力して下さい> ");

  value = scanf("%d%d", &a, &b );
  printf("scanf の値は: %d (a:%d, b:%d)\n", value, a, b );
}

補足:scanf()の戻り値は
・正常に読み込まれた数を返す。例えば scanf("%d%d",&i, &j) の場合、通常
   0(全く読めなかった)
   1(1個だけ正常に読めた)
   2(2個とも正常に読めた)
 のどれかが返ります。
・コントロール + d が押された場合やファイルの終端の場合 は特別に -1 (マクロ定義された EOFの値)が返ります (ハンドアウトlec10-16ページ参照)




中級レベル

(B-1) 関数を用いたプログラムの様々な間違い
以下は半径rの円の面積を計算し、小数第4位で四捨五入する関数 を利用したプログラムであるが、どうやら 正しく動作していないようである。 どのような結果が得られれば良いのか考え、予想して、 このプログラムの動作確認を行ない、正しく動作するように 修正しなさい。

ファイル名: ex10b1.c
#include <stdio.h>
#define PI 3.141592
int cirarea_r4(float);

main()
{
  float x;
  float menseki;

  printf("円の半径を入力して下さい ");
  scanf("%f",&x);
  menseki=cirarea_r4(x);
  printf("半径%fの円の面積は%fです\n",x, menseki);
}

int cirarea_r4(float r)
{
    r=PI*r*r;
    r=(int)(r*1000+0.5)/1000;
}


(B-2)華氏温度と摂氏温度の変換
華氏温度を摂氏温度に変換する関数を定義し、それを用いて実行例のように 0度から100度まで5度きざみの 華氏温度を摂氏温度に変えて表示するプログラムを作成しなさい。 なお、関数の中では計算のみを行ない、表示は実行例を 参考にmain()の中で行なうこと。

ファイル名: ex10b2.c

華氏温度から摂氏温度への変換式:
   c = 5 * (f - 32) / 9
ただし、華氏温度 f, 摂氏温度 c とします。 また、浮動小数点型で計算すること。

[実行例]
% ./a.out
華氏(°F)       摂氏(°C)
          0      -17.777779
          5      -15.000000
         10      -12.222222
         15       -9.444445
         .....
         90       32.222221
         95       35.000000
        100       37.777779



(B-3)べき乗関数の作成
べき乗(xp)を計算する関数 powint(int  x, int p) を定義し、それを用いて実行例のように3の0乗から3の10乗までを 計算しなさい。 なお、関数の中では計算のみを行ない、表示は実行例を 参考にmain()の中で行なうこと。 また、数学関数ライブラリのベキ乗関数pow()を使わず、 ループを用いること。

ファイル名: ex10b3.c
[実行例]
% ./a.out
3 の 0 乗は 1 です
3 の 1 乗は 3 です
3 の 2 乗は 9 です
3 の 3 乗は 27 です
3 の 4 乗は 81 です
3 の 5 乗は 243 です
3 の 6 乗は 729 です
3 の 7 乗は 2187 です
3 の 8 乗は 6561 です
3 の 9 乗は 19683 です
3 の 10 乗は 59049 です

注意)p(べき乗) が大きすぎるとオーバーフローが 発生して計算できなくなります。




上級レベル

(C-1) 指定された正の実数 a の n 乗根の計算
ハンドアウトlec10-21,23ページで述べたプログラムを もとに Newton法を用いて指定された 正の実数 a の n乗根を 求められるように f(), df() を修正しなさい。 ただし、 nは自然数であり a も n も main() で与えることとします。 また f(), df() には 元の引数だけでなく n も 渡せるようにし、 f(), df() の中で使われる x の べき乗の計算 ( xn) には for ループを 用いなさい。

ファイル名: ex10c1.c
[実行例]
% ./a.out
正の実数 a の n 乗根を求めます。
a, n を入力して下さい: 2.0  2
x               fx              dfx
1.500000        2.000000        4.000000
1.416667        0.250000        3.000000
1.414216        0.006944        2.833333
1.414214        0.000006        2.828431
2.000000 の 2 乗根は 1.414214

% ./a.out
正の実数 a の n 乗根を求めます。
 a, n を入力して下さい: 16.0  4
x               fx              dfx
12.000977       65520.000000    16384.000000
9.003047        20726.750000    6913.687500
6.757767        6553.889648     2918.962646
5.081286        2069.512451     1234.438843
3.841454        650.645142      524.784546
2.951653        201.762115      226.749710
2.369287        59.903343       102.862167
2.077716        15.511642       53.200199
2.004253        2.635670        35.877216
2.000014        0.136520        32.204563
2.000000        0.000435        32.000652
16.000000 の 4 乗根は 2.000000


(C-2)階乗の計算
階乗関数kaijo() の作成
自然数 n の階乗 (n!= n * (n-1)* ... * 3 * 2 * 1) を計算する関数 kaijo() を定義して、 n を入力すると n!を出力するプログラムを作りなさい。 n の入力と n! の値の出力は、main()で行ってください。

ファイル名: ex10c2.c
[動作確認用計算例]
0!=1, 1!=1, 2!=2, 3!=6, 4!=24, 5!=120, 6!=720, 7!=5040, …

注意)n が大きすぎるとオーバーフローが 発生して計算できなくなります。



(C-3)自然対数の底 eの計算
(C-2)で作成した n の階乗を求める関数 kaijo() を用いて 自然対数の底 e を求めるプログラムを 作りなさい。ただし、結果は級数の第10項(1/9!)まで ループを使用して計算することとし、 項数についてはマクロを用いて定義してください。 また、e の計算結果の出力は、main()で行うこと。

ファイル名: ex10c3.c

なお、eの級数は以下のように求められ、
e = 1 + 1/1! + 1/2! + 1/3! + 1/4! + ... +1/n! + ...
答えは 2.7182…になります。



(C-4)簡単な数値積分
関数f(x)の区間[a, b]における定積分の近似値は、以下のような式で計算できます。



この式(Simpsonの式)を利用して、定積分をするプログラムを作成してみましょう。

ファイル名: ex10c4.c
#include <stdio.h>
#include <math.h>

float func(float);
float simpson(float, float);
float integrate(float, float, int);

main()
{
  int i;
  float result;

  for(i = 100; i <= 3000; i += 100) {
    result = integrate(0.0, 1.0, i);
    printf("%4i分割 答え %10.8f (%10.8f)\n", i, result, 4.0*result);
  }
}

float func(float x)
{
  return sqrt(1.0-x*x);
}

float simpson(float a, float b)
{
  /* Simpsonの式の計算 */
}

float integrate(float p, float q, int n)
{
  /* 区間[p, q]をn分割して積分をする */
}
このプログラムでは関数を三つ定義しています。
  1. 関数func()は、xでの関数値f(x)を計算して返します。
  2. 関数simpson()は、与えられた区間[a,b]に対して、Simpsonの式を計算して返します。
  3. 関数integrate()は、与えられた区間[p,q]をn分割して、 それぞれの部分にSimpsonの式を適用し、その総和を計算して返します。
以上の定義にもとづいて、このプログラムを完成させてください。
この問題では、被積分関数を"sqrt(1.0 - x*x)"としているので、 "integrate(0.0, 1.0, n)"の値は円周率の四分の一の近似値となります。

関数sqrt()は、平方根を返します。この関数を使うためには、ソースプログラムの先頭に
#include <math.h>
を追加すると共にコンパイル時のオプションとして -lm を付ける必要があります(参考:ハンドアウトLec10-24)。
コンパイル例:
% gcc ex10c4.c  -lm



main()において、分割数を増やしながらintegrate()を計算しています。 分割数を増やすほど、答えが真の値に近づいています。
[実行例]
% gcc ex10c4.c -lm
% ./a.out
 100分割 答え 0.78535742 (3.14142966)
 200分割 答え 0.78538370 (3.14153481)
 300分割 答え 0.78539044 (3.14156175)
 400分割 答え 0.78539336 (3.14157343)
 500分割 答え 0.78539455 (3.14157820)
 600分割 答え 0.78539550 (3.14158201)
 700分割 答え 0.78539592 (3.14158368)
 800分割 答え 0.78539634 (3.14158535)
 900分割 答え 0.78539681 (3.14158726)
1000分割 答え 0.78539586 (3.14158344)
1100分割 答え 0.78539699 (3.14158797)
1200分割 答え 0.78539765 (3.14159060)
1300分割 答え 0.78539795 (3.14159179)
1400分割 答え 0.78539759 (3.14159036)
1500分割 答え 0.78539693 (3.14158773)
1600分割 答え 0.78539824 (3.14159298)
1700分割 答え 0.78539813 (3.14159250)
1800分割 答え 0.78539705 (3.14158821)
1900分割 答え 0.78539741 (3.14158964)
2000分割 答え 0.78539658 (3.14158630)
2100分割 答え 0.78539687 (3.14158750)
2200分割 答え 0.78539765 (3.14159060)
2300分割 答え 0.78539819 (3.14159274)
2400分割 答え 0.78539675 (3.14158702)
2500分割 答え 0.78539902 (3.14159608)
2600分割 答え 0.78539765 (3.14159060)
2700分割 答え 0.78539556 (3.14158225)
2800分割 答え 0.78539735 (3.14158940)
2900分割 答え 0.78539777 (3.14159107)
3000分割 答え 0.78539860 (3.14159441)



オプション問題(採点対象外)
(D-1)数当て(Hit & Blow)ゲームの作成

ファイル名: ex10d1.c

説明はこちら



何か疑問や間違いがあれば以下に連絡して下さい。
プログラミング入門演習問題担当(prog0)