レポート見本 1

データ構造とアルゴリズム I レポート課題

題名
成績処理
学籍番号
09nc999
氏名
坂本直志

課題1

レポート締切日: 2010年6月17日(木)20:00
提出先: 7 号館 3F レポートボックス

次の 4 つの課題を合わせて行い、報告しなさい。

なお、遅れレポートは教員に直接提出してください。 遅れた方が有利にならない範囲内で採点します。

構造体 SEISEKI が定義され、その配列がグローバルに定義されているとします。 但し、配列の番兵は文字ポインタ型メンバに NULL が入っているものとします。 (なお、配列の先頭には init 関数により学籍番号から計算した値を入れます)


#include <stdlib.h>
typedef struct seiseki {
  char* id;
  int suugaku;
  int eigo;
  int rika1;
  int rika2;
} SEISEKI;
SEISEKI soten[]={
  {NULL},
  {"kc10001",80,60,70,60},
  {"kc10002",70,90,50,100},
  {"kc10003",100,50,70,30},
  {"kc10004",20,30,40,50},
  {"kc10005",70,70,70,70},
  {"kc10006",60,65,70,75},
  {NULL}};
char id0[10];
double getTensu(){
  return (double)rand()/RAND_MAX*101;
}
void init(int no){
  srand(no);
  sprintf(id0,"kc10%03d",no+200);
  soten[0].id=id0;
  soten[0].suugaku=getTensu();
  soten[0].eigo=getTensu();
  soten[0].rika1=getTensu();
  soten[0].rika2=getTensu();
}

この時、この配列に対して成績処理をするプログラムを考えます。 但し、このデータはあくまでもテスト用のデータであるので、データが 7 こ しかないことを仮定するなど、データの自由度を奪うようなプログラムを作成 してはならない。

課題1-1

点数の集計方法を決める double kijun(SEISEKI s) を作ります。 ここでは、構造体 s に対して、suugaku の1.5 倍したものと、eigo はそのま ま、 rika1 とrika2 は多い方だけの3つの数の合計を double 型で返す関数を 作ります。 そして、以下のプログラムと結合して正しいかどうかを確かめなさい。

テストプログラム


#include <stdio.h>
#include <stdlib.h>
typedef struct seiseki {
  char* id;
  int suugaku;
  int eigo;
  int rika1;
  int rika2;
} SEISEKI;
double kijun(SEISEKI s){
/* ここにプログラムを書く */
}
int main(void){
  SEISEKI s1={"kc10001",1,2,3,4};
  SEISEKI s2={"kc10002",5,6,8,7};
  printf("%f %f\n",kijun(s1),kijun(s2));
  return 0;
}

これにより、 7.5 と 21.5 が出れば良い。

課題 1-2

学籍番号と各点数とそれから計算される kijun 点数を表示する hyouji(SEISEKI* p) を作成しなさい。 p は SEISEKI の配列を指します。 一つの学籍番号に対する成績は一行に表示するようにします。 そして、以下のプログラムと結合し、出力結果が以下の出力例と同じようにになるこ とを確認しなさい。 但し、下記のプログラムにおいて、 init 関数の引数には学籍番号の下3桁を 入れて実行しなさい。

プログラム


#include <stdio.h>
#include <stdlib.h>
typedef struct seiseki {
  char* id;
  int suugaku;
  int eigo;
  int rika1;
  int rika2;
} SEISEKI;
SEISEKI soten[]={
  {NULL},
  {"kc10001",80,60,70,60},
  {"kc10002",70,90,50,100},
  {"kc10003",100,50,70,30},
  {"kc10004",20,30,40,50},
  {"kc10005",70,70,70,70},
  {"kc10006",60,65,70,75},
  {NULL}};
char id0[10];
double getTensu(){
  return (double)rand()/RAND_MAX*101;
}
void init(int no){
  srand(no);
  sprintf(id0,"kc10%03d",no+200);
  soten[0].id=id0;
  soten[0].suugaku=getTensu();
  soten[0].eigo=getTensu();
  soten[0].rika1=getTensu();
  soten[0].rika2=getTensu();
}

ここまでは上で示した物と同じ


double kijun(SEISEKI s){
 /* 課題 1-1 で作成したもの */
}
void hyouji(SEISEKI* p){
 /* ここにプログラムを書く */
}
int main(void){/* テストプログラム */
  init(000 /* 学籍番号を入れる */ );
  hyouji(soten);
  return 0;
}

出力例

kc10200: 84 39 79 80 245.000000
kc10001: 80 60 70 60 250.000000
kc10002: 70 90 50 100 295.000000
kc10003: 100 50 70 30 270.000000
kc10004: 20 30 40 50 110.000000
kc10005: 70 70 70 70 245.000000
kc10006: 60 65 70 75 230.000000

最初の行は各自の学籍番号により異なります。

課題 1-3

課題 1-2 で表示された kijun の値について、手計算、電卓、または表計算ソ フトにより、平均値を求めなさい。

課題 1-4

人数を求める関数 int ninzu(SEISEKI* p) を作成しなさい。 そして下記のプログラムを実行し、出力が 7 になることを確認しなさい。

テストプログラム

上記のプログラムの main 関数部分だけを差し替えます。


int main(void){
  init(000 /* 学籍番号を入れる */ );
  printf("%d\n",ninzu(soten));
  return 0;
}

課題1-5

各 kijun で計算した値を元に、平均点を返す double heikin(SEISEKI* p) を 作成しなさい。 そして次のテストプログラムと結合して、結果を表示させなさい。 そして、課題 1-3 で求めた平均値と一致しているか確かめなさい。

テストプログラム


int main(void){
  init(000 /* 学籍番号を入れる */ );
  printf("%f\n",heikin(soten));
  return 0;
}

基礎知識

ポインタ

C 言語では変数の格納されている番地を扱うことができる。 変数名の前に & を付けると、その変数の番地を意味する。 また、変数の番地を取り扱う変数も用意されている。 そのような変数をポインタと言う。 ポインタは格納される値の型で区別される。 int の値が入る変数のポインタは int 型のポインタなどと言う。 そして int 型のポインタの宣言は int *ポインタ名 のよ うにする。 ポインタに対して、その番地に入っている値は*ポインタ名で表す。 以下に簡単な例を示す。


int x, *y;
x=1;
y=&x;
printf("%d\n",*y); /* 1 が表示される */

C 言語では配列変数はポインタにより実装されている。 配列 a[0], a[1] に対して、 a は a[0] の番地を示している。 つまり a と &a[0] は同じになる。 また、ポインタに対しても [値] とすると配列と同じように要素にアクセスで きる。 さらに、ポインタに 1 を足すとメモリの番地として 1 増えるのではなく、配列 として次の要素を指すようになる。 つまり次のような操作が許される。


int a[2];
int *x;
a[0]=5;
a[1]=6;
x=a;
printf("%d\n",*x); /* 5 が表示される */
printf("%d\n",x[1]); /* 6 が表示される */
x+=1;
printf("%d\n",*x); /* 6 が表示される */

つまり配列の宣言とポインタの宣言は領域を確保するかしないかだけで、基本 的には同じような意味となっている。

関数

C 言語ではプログラムを分割、再利用するために関数と言う概念がある。 関数は関数名(引数1,引数2, ...)という形で呼び出すと値を返す。 返してきた値を変数に代入するには変数=関数名(引数1,引数2, ...)という構文になる。 C 言語でプログラムを実行する時に呼び出されるのが main 関数である。

一方、関数の内部で定義された変数はローカル変数と呼び、他の関数か らアクセスできない。 関数の外側でも変数の定義ができる。 外側で定義した変数をグローバル変数と呼ぶ。 グローバル変数にはあらゆる関数からアクセスができるので、関数の情報を受 け渡すのに使用できるが、受渡しがうまく行ってない場合に原因を突き止める のが難しい。

C 言語の関数は値呼び出しである。 つまり関数の引数は関数側にとっては値が代入されているローカル変数となる。 したがって、関数の内部で値を変更しても、呼び出し側の引数の値を変更すること はできない。 そのため、変数の内容を関数で変更させるには、変数の番地を関数に与え、関数 内部ではポインタにより変数にアクセスする必要がある。

番兵

番兵とは複数のデータを扱う際に、データの終りにさらにデータの終りを示す データを与えるものである。 番兵を用いることによりデータの個数を意識しなくても複数のデータを処理す ることができるようになる。 配列変数を関数で処理する場合も、番兵を与えておけばデータの個数を関数に 渡す必要がなくなる。

なお、C言語では言語仕様で決められた番兵もある。 文字列では \0 、ファイルでは EOF である。

構造体

(ここに構造体の説明を書く)

プログラムの解法

課題1-1

関数 kijun は構造体 SEISEKI の仮引数 s をとる。 すると、s.id, s.suugaku, s.eigo, s.rika1, s.rika2 でそれぞれの値を参照できる。 したがって、この値を用いて設問通りに式を計算して値を返すようにした。 プログラムは付録に示した。 これを指示されたテストプログラムを用いてテストを行う。 行った結果も付録に示した。 指定された出力例と一致した。

課題1-2

関数 hyouji は構造体 SEISEKI の配列の仮引数 p をとる。 p->id, p->suugaku, p->eigo, p->rika1, p->rika2 でそれぞ れ配列の先頭の値を参照できる。 課題 1-1 で作成した kijun を呼び出すには、引数に *p を与える必要がある。 一方、p++ で次の配列の要素を指すようになる。 また、番兵は p->id が NULL の時で、これはデータの終了を表す。 以上を考慮して、 p が番兵に達するまでループを回し、それぞれの値を表示 する関数を付録に示した。 これを指示されたテストプログラムを用いてテストを行う。 行った結果も付録に示した。 レポート作成者の学籍番号は xxx であり、その場合の出力も表示されている ので、プログラムは正しく作成されたと考えられる。

課題1-3

課題 1-2 の出力を元に、電卓で平均値を求めたところ xx.xxxx になった。

課題1-4

省略

課題1-5

省略

まとめ

今回は構造体 SEISEKI の配列に対して成績処理を行う、 kijun, hyouji, ninzu, heikin の各関数を作成した。

作成するに当たって、とくに(省略)

参考文献

  1. 講義資料 http://edu.net.c.dendai.ac.jp/ad/2/2010/
  2. B.W.カーニハン, D.M.リッチー著, 石田晴久訳「プログラミング言語C」第二版。共立出版(1989)

付録

課題1-1のプログラム

課題1-1のテスト結果

課題1-2のプログラム

課題1-2のテスト結果

課題1-4のプログラム

課題1-4の実行例

課題1-5のプログラム

課題1-5の実行例

坂本直志 <sakamoto@c.dendai.ac.jp>
東京電機大学工学部情報通信工学科