本講義では 2 通のレポートで評価します。 レポートは A4 の紙を縦に使い、適宜表紙を付けて提出すること。 またプログラムは C 言語で作成しなさい。
なお、遅れレポートは原則受けとりません。
課題 2-2 の最後の出力文に誤りがあったため、修正しました(2010/7/7)。
stringToChararray で得た文字配列を正しく free するように、修正しました(2010/7/8)。
可変長文字列を扱うため、線形リストによる実装を考える。 下記の構造体を定義し、String 型変数を文字列として扱うこととする。
typedef struct node {
struct node * next;
char moji;
} Node;
typedef struct string {
Node * top;
} String;
与えられた String 型文字列に対して、文字列の文字数を返す関数
int stringLength(String s)
を作りなさい。
そして、下記のテストプログラムで正常に動作することを確かめなさい。
#include <stdio.h>
#include <stdlib.h>
typedef struct node {
struct node * next;
char moji;
} Node;
typedef struct string {
Node * top;
} String;
int stringLength(String s){
/* ここにプログラムを書く */
}
int main(void){
Node nil,a,b,c;
String s;
nil.next=NULL;
nil.moji='\0';
c.next=&nil;
c.moji='C';
b.next=&c;
b.moji='B';
a.next=&b;
a.moji='A';
s.top=&a;
printf("%d\n",stringLength(s));
return 0;
}
3
与えられた String 型文字列に対して、通常の C 言語の文字配列を返す関数
char* stringToChararray(String s)
を作りなさい。
そして、下記のテストプログラムで正常に動作することを確かめなさい。
なお、 malloc でメモリを確保できないときは exit(1) で終了しなさい。
#include <stdio.h>
#include <stdlib.h>
typedef struct node {
struct node * next;
char moji;
} Node;
typedef struct string {
Node * top;
} String;
int stringLength(String s){
/* 課題 2-1 のプログラム */
}
char * stringToChararray(String s){
/* ここにプログラムを書く */
}
int main(void){
Node nil,a,b,c;
String s;
char *carray;
nil.next=NULL;
nil.moji='\0';
c.next=&nil;
c.moji='C';
b.next=&c;
b.moji='B';
a.next=&b;
a.moji='A';
s.top=&a;
carray=stringToChararray(s);
printf("%s\n",carray);
free(carray);
return 0;
}
ABC
C 言語の文字配列を String 型に変換する
String chararrayToString(char * s)
を作りなさい。
そして、下記のプログラムと結合して、正常に動作することを確認しなさい。
なお、 malloc でメモリを確保できないときは exit(1) で終了しなさい。
#include <stdio.h>
#include <stdlib.h>
typedef struct node {
struct node * next;
char moji;
} Node;
typedef struct string {
Node * top;
} String;
int stringLength(String s){
/* 課題 2-1 のプログラム */
}
char* stringToChararray(String s){
/* 課題 2-2 のプログラム */
}
String chararrayToString(char* s){
/* ここにプログラムを書く */
}
void deleteNodes(Node *p){
if(p->next==NULL) return;
deleteNodes(p->next);
free(p);
}
void deleteString(String s){
deleteNodes(s.top);
}
int main(void){
String s;
char *carray;
s=chararrayToString("ABCD");
carray=stringToChararray(s);
printf("%s\n",carray);
free(carray);
deleteString(s);
s=chararrayToString("");
carray=stringToChararray(s);
printf("%s\n",carray);
free(carray);
deleteString(s);
return 0;
}
ABCD
String である dest, source に対して、 dest の直後に source をつなげる
void appendString(String *dest, String source)
関数を作りなさい。
そして、以下のプログラムと結合し、正しく動作することを確かめなさい。
#include <stdio.h>
#include <stdlib.h>
typedef struct node {
struct node * next;
char moji;
} Node;
typedef struct string {
Node * top;
} String;
int stringLength(String s){
/* 課題 2-1 のプログラム */
}
char* stringToChararray(String s){
/* 課題 2-2 のプログラム */
}
String chararrayToString(char* s){
/* 課題 2-3 のプログラム */
}
void appendString(String *dest, String source){
/*ここにプログラムを書く*/
}
void deleteNodes(Node *p){
if(p->next==NULL) return;
deleteNodes(p->next);
free(p);
}
void deleteString(String s){
deleteNodes(s.top);
}
int main(void){
String s,t;
int i;
char *carray;
s=chararrayToString("");
for(i=0; i<4; i++){
t=chararrayToString("ABCD");
appendString(&s,t);
}
carray=stringToChararray(s);
printf("%s\n",carray);
free(carray);
deleteString(s);
return 0;
}
ABCDABCDABCDABCD
なお、次の関数を利用するとデータ構造がよく分かりプログラムの動きがつか みやすくなるので、適宜利用してよい。
void printStructure(String s){
Node *p;
printf("start:%p\n",s.top);
p=s.top;
while(p!=NULL){
printf("next=%p, moji=%c\n",p->next,p->moji);
p=p->next;
}
printf("---------------\n");
}
次の 4 つの課題を合わせて行い、報告しなさい。
なお、遅れレポートは教員に直接提出してください。 遅れた方が有利にならない範囲内で採点します。
注: init の引数に学籍番号の下 3 桁を入れる場合、先頭の 0 を取り除いて 入れてください。先頭に 0 がある整数は 8 進数として別の数に解釈されてし まいます(2010/6/15)。
なお、1-5 のテストプログラムに一行追加しました。 (2010/6/10)
構造体 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 こ しかないことを仮定するなど、データの自由度を奪うようなプログラムを作成 してはならない。
点数の集計方法を決める 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 が出れば良い。
学籍番号と各点数とそれから計算される 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 /* 学籍番号を入れる */ );
/* 先頭の 0 は取り除いてください */
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-2 で表示された kijun の値について、手計算、電卓、または表計算ソ フトにより、平均値を求めなさい。
人数を求める関数 int ninzu(SEISEKI* p) を作成しなさい。 そして下記のプログラムを実行し、出力が 7 になることを確認しなさい。
上記のプログラムの main 関数部分だけを差し替えます。
int main(void){
init(000 /* 学籍番号を入れる */ );/* 先頭の 0 は取り除いてください */
printf("%d\n",ninzu(soten));
return 0;
}
各 kijun で計算した値を元に、平均点を返す double heikin(SEISEKI* p) を 作成しなさい。 そして次のテストプログラムと結合して、結果を表示させなさい。 そして、課題 1-3 で求めた平均値と一致しているか確かめなさい。
int main(void){
init(000 /* 学籍番号を入れる */ );/* 先頭の 0 は取り除いてください */
printf("%f\n",heikin(soten));
return 0;
}
マイクロソフトの Visual Studio で上記のプログラムを動かすと大抵 suugaku が 0 点になってしまいますが、これは Visual Studio の乱数生成の問題です。 データが 0 点なのは問題ありません。