Ex11
From Prog0
演習第11回
Contents |
演習問題
主な内容
- 関数の呼び出しと定義
- 引数・仮引数・戻り値
- プロトタイプ宣言
なお、一人ひとりの授業理解度を確認することを目的として、口頭試問形式による採点を行います。
採点対象の問題が解けたら手を挙げ、教員またはTA/SAを呼んでください。口頭で解答の内容を説明してもらいます。
正しく説明できた場合は点数を付け、説明が不十分な場合はやり直してもらいます(間違えても減点はしません)。
今回の口頭採点の対象は A-2 です。
(口頭採点の対象も、その他の問題と同様、menuコマンドで提出してください。)
出席確認
演習時間中に出席確認をLMS上の「演習出欠」で行ってください。出席確認用のパスワードは演習時間のどこかのタイミングで提示されます。
A問題
A-1 関数の呼び出し
ファイル名: ex11a1.c
以下はプロトタイプ宣言されている4つの関数を使って、4つの文(メッセージ)を出力するプログラムである。下線部を補い、実行例の通りに動作するよう、プログラムを完成させなさい。
#include <stdio.h>
void print_function1(void);
void print_function2(int);
void print_function3(void);
void print_function4(void);
int main()
{
print_function1();
print_function2(____);
print_function3();
return 0;
}
______ print_function1(void)
{
printf("Hey guys!\n");
}
______ print_function2(______)
{
printf("I just hit %d followers!\n", num);
}
______ print_function3(_____)
{
printf("Thank you so much!\n");
____________________
}
______ print_function4(void)
{
printf("_________\n");
}
[実行例]
% ./a.out
Hey guys!
I just hit 2000 followers!
Thank you so much!
See you!
%
A-2 (口頭試問)関数の基礎
ファイル名: ex11a2.txt
以下の問いに答えなさい。答えは ex11a2.txt に記入すること。
- 関数を使用することによる利点とは?
- 実引数と仮引数の違いは何か?
- 戻り値とは何か?
- ローカル変数とは何か?
A-3 関数のプロトタイプ宣言と関数定義
ファイル名: ex11a3.c
sin関数やcos関数などの三角関数は2πの周期があり、また様々な対称性がある。
このような関数をプログラムで計算する際に、引数の区間を限定することで高速化できる場合がある。これを引数のリダクションと呼ぶ。
例えば周期のみを考慮すると、sin関数の場合、0から2πの区間での値が計算できれば十分である。 さらに対称性を考慮すると、0からπ/2の区間に引数をリダクションすることもできる。
ここでは、前者の場合について考える。
以下のプログラムは、二つの関数yukaとargredを用いて、引数のリダクションによりsin関数の値を求め、通常の計算方法を用いた場合と比較するものである。
yuka関数は、実数型の引数を二つとり(仮にz, wとする)、zをwで割った値以下の最大の整数を計算して戻り値とする関数である(なお、ある実数について、その実数を超えない最大の整数を返す関数を床関数と呼ぶ)。
argred関数は、実数型の引数を二つとり(仮にx, yとする)、周期yの場合の引数xのリダクションを計算して戻り値とする関数である。
sin関数を例に考えると、その周期は2πなので、argred関数の中では以下の式でxのリダクション(x')を計算する。この時、yuka関数を呼び出していることに注意せよ。
x' = x - 2π * yuka(x, 2π)
この式により、x'の値は0から2πの区間にリダクションされる。
argred関数とyuka関数のプロトタイプ宣言と定義を追加して、以下のプログラムを完成させなさい。
#include <stdio.h>
#include <math.h>
/* 関数のプロトタイプ宣言を書く */
int main()
{
double x, y, xdash;
scanf("%lf", &x);
y = 2.0*M_PI;
printf("x = %18.15e: yuka(x, 2pi) = %d\n\n", x, yuka(x, y));
xdash = argred(x, y);
printf("x = %18.15e: sin(x ) = %18.15f\n", x, sin(x));
printf("xdash = %18.15e: sin(xdash) = %18.15f\n", xdash, sin(xdash));
return 0;
}
/* 関数の定義を書く */
注1:M_PIは、ヘッダファイルmath.hで定義されている倍精度での円周率のマクロである。
注2:ライブラリのsin関数を使っているため、
% gcc ex11a3.c -lm
のように"-lm"のオプションを付加してコンパイルする必要がある。
[実行例]
% ./a.out1.5x = 1.500000000000000e+00: yuka(x, 2pi) = 0 x = 1.500000000000000e+00: sin(x ) = 0.997494986604054 xdash = 1.500000000000000e+00: sin(xdash) = 0.997494986604054% ./a.out100.0x = 1.000000000000000e+02: yuka(x, 2pi) = 15 x = 1.000000000000000e+02: sin(x ) = -0.506365641109759 xdash = 5.752220392306214e+00: sin(xdash) = -0.506365641109749% ./a.out1.5e5x = 1.500000000000000e+05: yuka(x, 2pi) = 23873 x = 1.500000000000000e+05: sin(x ) = 0.998562008266105 xdash = 1.517161701747682e+00: sin(xdash) = 0.998562008266943
sin(xdash)の値は、実行環境により数値が異なる場合がある。
B問題
B-1 数字ピラミッドの作成
ファイル名: ex11b1.c
1〜9の整数nを入力し、各行に行番号(1, 2, 3, ...)の数字をその回数だけ繰り返して、中央揃えの数字ピラミッドを表示するプログラムを作成しなさい。数字ピラミッドの出力は、関数「void print_pyramid(int n)」を作成して実行し、数字と数字の間には1つの空白を入れなさい。また、n が1〜9の範囲外であれば「範囲外の数値です。」と表示して、プログラムを終了しなさい。
[実行例]
% ./a.out 1〜9の整数を入力してください: 5 1 2 2 3 3 3 4 4 4 4 5 5 5 5 5 %% ./a.out 1〜9の整数を入力してください: 8 1 2 2 3 3 3 4 4 4 4 5 5 5 5 5 6 6 6 6 6 6 7 7 7 7 7 7 7 8 8 8 8 8 8 8 8 %% ./a.out 1~9の整数を入力してください: 13 範囲外の数値です。 %
B-2 温度変換
ファイル名: ex11b2.c
温度を摂氏から華氏に変換する関数 c2f を定義し、それを使って 0 度から 150 度まで 5 度刻みの摂氏温度と、それに対する華氏温度を表示するプログラムを作成しなさい。関数 c2f は、摂氏温度(整数値)を引数とし、華氏温度(実数値)を戻り値とする。また、結果の表示は関数 c2f ではなくmain関数で行うこと。
摂氏 (c) から華氏 (f) への温度の変換式は以下の通り:
f = 9*c/5 + 32
[実行例]
% ./a.out
摂氏(°C) 華氏(°F)
0 32.00
5 41.00
10 50.00
15 59.00
.....
140 284.00
145 293.00
150 302.00
%
B-3 関数の応用
ファイル名: ex11b3.c
今回のハンドアウトで説明した関数のうち、円の面積を計算する関数(関数nijouを利用する版)circle1と円柱の体積を計算する関数cylinderを用いて、キーボードから入力された半径の値と円柱の高さから、円の面積と円柱の体積を計算して表示するプログラムを作成せよ。
ただし、circle1とcylinderの二つの関数はハンドアウト(Lec11-19)に掲載されているものをそのまま用いること。
また、二乗を計算する関数nijouは、ハンドアウトに掲載されているものは用いずに、ヘッダファイルmath.hに定義されている、べき乗を計算する関数
double pow(double x, double y) : x^yを計算する
を利用して追加で作成すること。その際、関数名、引数の型と数、戻り値の型はハンドアウトに掲載されているnijou関数と同じにしておけば、circle1関数内ではそのままnijou関数を呼び出すことができる。
[実行例]
% ./a.out
円の半径を入力してください:2
円の面積:12.560000
この円を底面とする円柱の高さを入力してください:3
円柱の体積:37.680000
%
注:ライブラリのpow関数を使っているため、
% gcc ex11b3.c -lm
のように"-lm"のオプションを付加してコンパイルする必要がある。
Extra問題
E-1 数値積分
ファイル名: ex11e1.c
関数 f(x) の区間 [a, b] における定積分の近似値は以下のSimpsonの式で計算できる。
この式を利用して定積分を計算するプログラムを作成し、例として円周率を求めてみる。
#include <stdio.h>
#include <math.h>
double func(double);
double simpson(double, double);
double integrate(double, double, int);
int main() {
int i;
double result;
/* 積分区間の分割数を変えて積分 */
for (i = 100; i <= 3000; i += 100) {
result = integrate(0.0, 1.0, i);
/* 答えと"答えx4"を表示します */
printf("%4d分割 答え %10.8f (%10.8f)\n", i, result, 4.0*result);
}
return 0;
}
/* 被積分関数の定義 */
double func(double x) {
return sqrt(1.0-x*x);
}
/* Simpsonの式の計算 */
/* func()を三回呼び出します */
double simpson(double a, double b) {
/* 区間[a, b]について、Simpsonの式を計算 */
}
double integrate(double p, double q, int n) {
/* 区間[p, q]をn分割して積分する */
}
このプログラムでは関数を3つ定義している。
- 関数double func(double x)は、xの関数値 f(x) を計算して返す。
- 関数double simpson(double a, double b)は、与えられた区間 [a,b] に対してSimpsonの式を計算して返す。
- 関数double integrate(double p, double q, int n)は、与えられた区間 [p,q] をn分割して、それぞれの部分にSimpsonの式を適用し、その総和を計算して返す。
以上の定義にもとづいて、関数simpsonと関数integrateの中身を補ってプログラムを完成させなさい。 なお、この問題では被積分関数をsqrt(1.0 - x*x)としているので、integrate(0.0, 1.0, n)の値は円周率の4分の1の近似値となる。
[実行例]
% ./a.out 100分割 答え 0.78535756 (3.14143025) 200分割 答え 0.78538381 (3.14153524) 300分割 答え 0.78539035 (3.14156140) ... 2800分割 答え 0.78539789 (3.14159156) 2900分割 答え 0.78539790 (3.14159161) 3000分割 答え 0.78539792 (3.14159167) %
課題提出上の注意事項
解答ファイルはmenuコマンドを使って提出してください。以下のようにmenuコマンドを実行し、表示されるメッセージに沿って操作すること。
% ~prog0/bin/menu
menuコマンドは、解答ファイルが ~/Prog0/Ex## のディレクトリに指定されたファイル名で置かれているものとして処理します。正常に提出された場合は ○ が、何らかのエラーが生じた場合は × が表示されます。
解答の提出期間は以下のとおりです。
| 問題 | 提出受付開始 | 提出〆切 |
|---|---|---|
| A問題 | 演習日の6日前の午後9時 | 演習終了時刻 |
| B, Extra問題 | 演習日の6日前の午後9時 | 演習日の6日後の午後9時 |
提出は〆切前であれば何度でもやり直すことができます。再提出すると、前に提出したファイルは新しい内容で上書きされます。
