プログラミングC
第6回 演習問題


A問題(50点)

演習問題1

1. 以下のプログラムは、キーボードから数値を3つ入力すると、それらの平均を計算して 表示するとともに、入力値から平均値を引き算した結果(+/-符号付き、小数点以下第3位まで) を表示するプログラムである。仕様に合うように、プログラムの空欄を埋めて完成させなさい。
(提出ファイル名: prog01a.c)

仕様 プログラムA:
#include <stdio.h>

______ avesub_ptr(________, ________, ________);   /* プロトタイプ宣言 */

int main()
{
  double a, b, c, ave;

  printf("3つ値を入力してください:\n");
  scanf("%lf%lf%lf", &a, &b, &c);

  _____ = avesub_ptr( _____, ______, _____ );   /* 関数の呼び出し */

  printf("平均:  %.3f\n", ave);
  printf("平均差し引き後のデータ:  %+.3f  %+.3f  %+.3f\n", a, b, c);
  return 0;
}

/* 平均を計算して各データから引き算する関数。戻り値はその平均値。 */
______ avesub_ptr(________, ________, ________)
{

  /* ここを適宜作成 */

}
実行例:
% ./a.out
3つ値を入力してください:
1.0 3.0 5.0
平均:  3.000
平均差し引き後のデータ:  -2.000  +0.000  +2.000
% ./a.out
3つ値を入力してください:
5.0 9.0 -6.5
平均:  2.500
平均差し引き後のデータ:  +2.500  +6.500  -9.000

2. 同じ機能をもつプログラムを、配列を利用して作成したい。 新しい仕様に合うように、プログラムの空欄を埋めて完成させなさい。
(提出ファイル名: prog01b.c)

仕様(変更部分のみ;他は前と同じ) プログラムB:
#include <stdio.h>

______ avesub_arr(________);   /* プロトタイプ宣言 */

int main()
{
  double a[3], ave;

  printf("3つ値を入力してください:\n");
  scanf("%lf%lf%lf", _____, _____, _____);

  _____ = avesub_arr( _____ );   /* 関数の呼び出し */

  printf("平均:  %.3f\n", ave);
  printf("平均差し引き後のデータ:  %+.3f  %+.3f  %+.3f\n", a[0], a[1], a[2]);
  return 0;
}

/* 平均を計算して各データから引き算する関数。戻り値はその平均値。 */
______ avesub_arr(__________)
{

  /* ここを適宜作成 */

}
実行例は前と同じである。

B問題(50点)

演習問題2

コマンドライン引数として得点(評点)をいくつか与えると、実行例のようにその評価 (A/B/C/D/F)を出力するプログラムを作成しなさい。 (得点と評価の対応は、 A: 80~100点, B: 65~79点, C: 50~64点, D: 35~49点, F: 35点未満)

なお、得点から評価への変換には、 演習第1回問題3 で作成した関数を流用してもよい。 また、コマンドライン引数には得点をいくつ書いても良いこととし、引数が無い場合は何もせずに終了する。 なお、得点として0から100までの整数以外が入力された場合の処理は考えなくてよい。 (提出ファイル名: prog02.c)

実行例:
% ./a.out  100  79  64  34  48
Score:100  Grade: A
Score: 79  Grade: B
Score: 64  Grade: C
Score: 34  Grade: F
Score: 48  Grade: D
% ./a.out
%
コマンドライン引数はあくまで文字列なので、 それを得点としての整数型の値に変換する必要がある。 変換を行うには、Lec06-14(p112)で紹介された関数 atoi() を用いると良い。関数 atoi の引数は文字ポインタ、戻り値は int型である。 なお、この関数を使用するには、 stdlib.h をプログラムの最初でインクルードしておく必要がある。

演習問題3

2つの数の間を等間隔に分割して、数値の列を作る事を考える。 例えば、0.0 から 2.0 まで等間隔に並ぶ5つの数値を作るとすると、 0 から 2 の間を(両端にも数値があるので)5-1 = 4つの区間に 等分することになるので、0.5 おきに数値を並べて、 0.0, 0.5, 1.0, 1.5, 2.0 となる。

それでは、最初の数値、最後の数値、作る値の個数、を入力すると、 「最初の数値」~「最後の数値」まで等間隔に並ぶ値が格納された配列を 動的メモリ割り当てによって作成し、格納された値すべてを表示し、 さらに各々の値の2乗も表示する(小数点以下3桁まで;実行例参照)プログラムを作成しなさい。

なお、「最初の数値」と「最後の数値」は浮動小数点数、「作る値の個数」は2以上の整数とする (エラーチェックはしなくてよい)。 動的メモリ割り当てで確保するメモリは「作る値の個数」に応じた必要最小限とすること。 (提出ファイル名: prog03.c)

実行例:
% ./a.out
Input First value, Last value, Total number: 0.0  2.0  5
Values of x
0.000 0.500 1.000 1.500 2.000
Values of x^2
0.000 0.250 1.000 2.250 4.000
% ./a.out
Input First value, Last value, Total number: 1.0  3.0  11
Values of x
1.000 1.200 1.400 1.600 1.800 2.000 2.200 2.400 2.600 2.800 3.000
Values of x^2
1.000 1.440 1.960 2.560 3.240 4.000 4.840 5.760 6.760 7.840 9.000
%
プログラム例:
#include <stdio.h>
#include ________

int main()
{
  int num, i;
  double f_value, l_value;
  double *x;

  /* 最初の値、最後の値、数値の個数を入力 */
  printf("Input First value, Last value, Total number: ");
  scanf("%lf%lf%d", &f_value, &l_value, &num);

  /* これ以降を作成すること */

  /* メモリ確保 */

 /* 格納する値の決定。
    端の値のぶんを考慮して分割数を決める事と、整数除算しないように注意 */

  /* 結果の表示 */

  /* メモリ確保したので後処理をする */
  
  return 0;
}

Extra問題

演習問題4

コマンドラインから複数の文字列が入力されたとき、その文字列から新たな文字列を表示するプログラムを、以下の方針に基づいて作成せよ。
(提出ファイル名: prog04.c)
実行例:
% ./a.out  test  text.in  x.in.in  hoge.out  prog.c
test -> test.out
text.in -> text.out
x.in.in -> x.in.out
hoge.out -> hoge.out.out
prog.c -> prog.c.out
%

演習問題5

テキストファイルに書かれている単語を読みこみ、 その単語を編集してファイルに保存するプログラムを作成する。 (提出ファイル名: prog05.c)

このプログラムはコマンドライン引数を2つ受け取り、 最初の引数を入力ファイル名、2番目の引数を出力ファイル名とする。
まず、入力ファイルから、空白で区切られた単語を1つずつ読みこんで記憶するが、 単語の長さはまちまちであるため、下図のような可変長二次元配列を使う。 この問題での可変長二次元配列は、1024要素のポインタの配列と、動的に確保したメモリ領域を使って実現する。 単語を読み込む手順は以下のようになる。

  1. ファイルから一次元配列(バッファと呼ぶ)に単語を1つ読む。バッファの大きさは256バイトとする。
  2. 読んだ文字数+1(ヌル文字の分)の大きさのメモリを動的に確保
  3. ポインタ配列の行数番目の要素に確保した領域のアドレスを代入
  4. バッファにある読みこんだ単語(最後にヌル文字)を、確保した領域にコピー
  5. 行数カウンタをインクリメント
  6. 1に戻る

図:可変長二次元配列

次に、以下のコマンドを受け付けながら、単語を編集していく。編集中は end 以外のコマンドは何度も受け付けるようにする。

最後に内容を保存するが、このとき可変長二次元配列に格納されているすぺての単語を、 空白区切りで出力ファイルに出力する。

実行例 (テスト用入力:uoa.in

% cat uoa.in
The University offers one of top-class computer education in Japan
% ./a.out uoa.in uoa.out
>show
   0: The
   1: University
   2: offers
   3: one
   4: of
   5: top-class
   6: computer
   7: education
   8: in
   9: Japan
>insert 2 of
>insert 2 Aizu
>show
   0: The
   1: University
   2: Aizu
   3: of
   4: offers
   5: one
   6: of
   7: top-class
   8: computer
   9: education
  10: in
  11: Japan
>swap 2 3
>remove 8
>show
   0: The
   1: University
   2: of
   3: Aizu
   4: offers
   5: one
   6: of
   7: top-class
   8: education
   9: in
  10: Japan
>end
% cat uoa.out
The University of Aizu offers one of top-class education in Japan
%
注意:必ず動的メモリ割り当てを使って単語を保持すること。 ポインタの配列に入っている要素を適切に移動することによって、単語を削除して 空いた要素を詰めたり、単語を挿入するために要素を空けたりすることができる。 また、単語を削除する際には、正しくメモリを解放すること。