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


A問題(50点)

演習問題1


講義で学んだ、関数の引数としてポインタを与える「アドレス渡し」と、関数の引数として配列の先頭アドレスを与える方法を確認しよう。

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

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

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

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

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

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

  printf("平均:  %.3f\n", ave);
  printf("平均からの差分:  %+.3f  %+.3f  %+.3f\n", a, b, c);
  return 0;
}

/* 平均を計算して戻り値とするとともに、ポインタ渡しされた値も平均からの差分に書き換える関数。 */
______ aveandsub_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>

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

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

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

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

  printf("平均:  %.3f\n", ave);
  printf("平均からの差分:  %+.3f  %+.3f  %+.3f\n", data[0], data[1], data[2]);
  return 0;
}

/* 平均を計算して戻り値とするとともに、ポインタ渡しされた値も平均からの差分に書き換える関数。 */
______ aveandsub_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
評点:100  評価: A
評点: 79  評価: B
評点: 64  評価: C
評点: 34  評価: F
評点: 48  評価: D
% ./a.out
%
コマンドライン引数はあくまで文字列なので、 それを得点としての整数型の値に変換する必要がある。 変換を行うには、Lec06-14(p112)で紹介された関数 atoi() を用いると良い。関数 atoi の引数は文字ポインタ、戻り値は int型である。 なお、この関数を使用するには、 stdlib.h をプログラムの最初でインクルードしておく必要がある。

演習問題3

2つの数値の間を等間隔に分割して、数値の列(つまり等差数列)を作る事を考える。 例えば、0.0 から始まり 2.0 で終わる5つの数値を作るとすると、 0.0 から 2.0 の間を 5-1 = 4つの区間に等分することになり、刻み(等差数列の公差に相当する)は 0.5 である。 よって、0.0, 0.5, 1.0, 1.5, 2.0 という数値の列が得られる。

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

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

実行例:
% ./a.out
最初の値, 最後の値, 個数を入力: 0.0  2.0  5
数値の列
0.000 0.500 1.000 1.500 2.000
2倍値
0.000 1.000 2.000 3.000 4.000
% ./a.out
最初の値, 最後の値, 個数を入力: 1.0  3.0  11
数値の列
1.000 1.200 1.400 1.600 1.800 2.000 2.200 2.400 2.600 2.800 3.000
2倍値
2.000 2.400 2.800 3.200 3.600 4.000 4.400 4.800 5.200 5.600 6.000 
%
プログラム例:
#include <stdio.h>
#include ________

int main()
{
  int num, i;
  double v0, vn;
  double *x;

  /* 最初の値、最後の値、数値の個数を入力 */
  printf("最初の数値, 最後の数値, 個数を入力: ");
  scanf("%lf%lf%d", &v0, &vn, &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
%
注意:必ず動的メモリ割り当てを使って単語を保持すること。 ポインタの配列に入っている要素を適切に移動することによって、単語を削除して 空いた要素を詰めたり、単語を挿入するために要素を空けたりすることができる。 また、単語を削除する際には、正しくメモリを解放すること。