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


A問題(50点)

演習問題1(構造体の基礎を学ぶ)

以下の問題を読み、prog01a.c, prog01.h, prog01b.cの3つのファイルを提出しなさい。
  1. 以下に示す構造体 my_record を宣言し、 main 関数中で、my_record 型構造体変数 data を定義する(外部変数にしないこと)。
    そして、標準入力(キーボード)からデータを入力すると、変数 data のメンバーに入力値を格納し、 変数 data のメンバーの内容を下記実行例のように出力するプログラムを作成せよ。
    なお、決められた配列のサイズを超える入力や決められた変数の型以外の入力は、起こりえないものとする。
    (提出ファイル名 prog01a.c
    ※また、この時点ではヘッダファイルは使用しないこと。
    /* 個人レコード */
    struct my_record{
    	char lastname[20];   /* 名字 */
    	char firstname[20];  /* 名前 */
    	char gender[7];      /* 性別 */
    	int birthday[3];     /* 生年月日(要素0: 西暦, 要素1: 月, 要素2: 日) */
    };
    
    struct my_record data; /* この1行は main関数に書いて外部変数にはしないこと */
    
    実行例
    % ./a.out
    データを入力して下さい
    名字 -> Smith
    名前 -> Olivia
    性別 (Male/Female) -> Female
    生まれた年 (西暦) -> 1988
    生まれた月 -> 2
    生まれた日 -> 24
    
    氏名: Smith Olivia
    性別: Female
    生年月日: 1988/2/24
    
  2. 上述の構造体 my_record 宣言部分を typedef を利用して、 以下のように書き直したものを prog01.h として保存する。
    そして、prog01a.c を、 prog01.h を使用した形に書き換えた prog01b.c を作成せよ。
    (提出ファイル名 prog01.h, prog01b.c
    /* 個人レコード */
    typedef struct {
        char lastname[20];   /* 名字 */
    	char firstname[20];  /* 名前 */
    	char gender[7];      /* 性別 */
    	int birthday[3];     /* 生年月日(要素0: 西暦, 要素1: 月, 要素2: 日) */
    }My_record;
    

B問題(50点)

演習問題2(関数の引数・戻り値として構造体を使用する方法を学ぶ)

以下の問題を読み、prog02a.c, prog02b.cの2つのファイルを提出しなさい。
  1. 演習問題1-2(prog01b.c)を 以下の3つの関数に分割した後、実行例のように入力された人の年齢を表示するプログラムを作成せよ。
    なお、決められた配列のサイズを超える入力や決められた変数の型以外の入力は、起こりえないものとする。
    また、前問で作成したヘッダファイル prog01.h は、そのまま使用すること。
    • My_record input(void) : 標準入力(キーボード)から構造体への入力
    • void output(My_record) : 構造体内容の標準出力への出力(ディスプレイへの表示)
    • int main() : 構造体変数 data を定義する。 inputoutput をそれぞれ1回ずつ呼ぶ
  2. さらに年齢を求める関数get_age(My_record) を 追加してmainから呼び出すこと。

演習問題3 (構造体と関数の応用を学ぶ)

前準備として、データ一行につき「学籍番号、名前、5教科の試験の得点」が書かれている成績ファイル score.txtを自分のディレクトリにコピーしておく。 (端末上で、コマンド cp /home/course/prog1/public_html/2024/ex/ex07/score.txt .で、 カレントディレクトリにファイルscore.txtをコピーすることができる)

79105001  Alexander 100  100  100  100  100
79105002  Mia        67   77   75   92   80
79105003  Mason      37   60   71   52   75
 (中略)
79105014  Ella       81   45   46   75   85
79105015  Sebastian  29   37   40   20   45
79105016  Elizabeth  95   97   90   95   90

以下の説明文を読み、題意を満たすプログラムを作成せよ。
(提出ファイル名 prog03.c

  1. 上記のデータをファイルから構造体配列に読み込む。配列の大きさは20とする。
  2. 科目数を表すマクロ SUBJECT_NUM を使用して、 データを格納する構造体の宣言は typedef によって以下のように行う。 (ヘッダファイルとして独立させてはいけない)
  3. #define SUBJECT_NUM 5 /* 科目数 */
    
    typedef struct{
    	char id[10];            /* 学籍番号 */
    	char name[10];          /* 名前 */
    	int score[SUBJECT_NUM]; /* 各点数 */
    	int sum;                /* 合計点 */
    	double ave;             /* 平均 */
    	char grade;             /* 判定 */
    }Record;
    
  4. コマンドラインオプションを使ってデータファイル名を入力する (コマンドラインオプションのプログラムへの渡し方については講義第6回参照)。 ファイル名が無い場合やファイルが見つからない場合はエラーメッセージを出力して終了する。
  5. 読み込みと同時に5教科の試験の合計点を構造体の要素(メンバー)sum に、 そして、平均点を要素 ave に代入する。 さらに、平均点に対して会津大学の判定基準でA~D、およびFの判定を行い要素grade に代入する。
  6. すべての人のすべてのデータ(5教科の試験の得点、合計点、平均、判定)を表示する。平均点は小数第一位の出力とする。
  7. その後、検索したい名前か番号を標準入力から入力し、リニアサーチを行う。 文字列の一致の判定にはstrcmp関数を使ってよい (最初にstring.hをインクルードしておくこと)。
    見つかった場合は、該当の人のすべてのデータ(5教科の得点、合計点、平均、判定)を表示する。 入力されたのが名前か番号かを区別するのは、最初の文字が英字か数字かを基準に判定すればよい。
  8. 判定基準A~D、およびFの判定は、構造体を引数に取る関数として実装すること。
  9. データの表示は、構造体を引数にとる関数として実装すること。 すべての人のすべてのデータを表示するときも、検索結果を表示するときも、この同じ関数を用いること。
  10. 実行例:
    % ./a.out score.txt
    79105001   Alexander  100  100  100  100  100  500  100.0  A
    79105002   Mia         67   77   75   92   80  391   78.2  B
    79105003   Mason       37   60   71   52   75  295   59.0  C
    79105004   Evelyn      79   61   82   70   75  367   73.4  B
    79105005   Michael     77   60   75   55   60  327   65.4  B
    79105006   Harper      88   66   75   45   70  344   68.8  B
    79105007   Ethan       43   79   64   18   65  269   53.8  C
    79105008   Camila      45   46   75   98   90  354   70.8  B
    79105009   Jacob       99   96   95   99   86  475   95.0  A
    79105010   Abigail     40   25   37   27   45  174   34.8  F
    79105011   Jackson     70   95   96   68   70  399   79.8  B
    79105012   Luna        34   58   52   45   55  244   48.8  D
    79105013   Levi        99   69   77   60   75  380   76.0  B
    79105014   Ella        81   45   46   75   85  332   66.4  B
    79105015   Sebastian   29   37   40   20   45  171   34.2  F
    79105016   Elizabeth   95   97   90   95   90  467   93.4  A
    Input a student name/ID: Ethan
    79105007   Ethan       43   79   64   18   65  269   53.8  C
    Input a student name/ID: Rosemary
    This student does not exist!
    Input a student name/ID: 79105001
    79105001   Alexander  100  100  100  100  100  500  100.0  A
    Input a student name/ID: Ctrl-D
    % ./a.out
    Error!  Usage: ./a.out datafilename
    % ./a.out sample.txt
    Cannot open data file !
    

Extra問題

演習問題4(二次元座標情報を格納する構造体を利用したプログラム)

以下の説明文を読み、題意を満たすプログラムを作成せよ。
(提出ファイル名 prog04.c)

  1. 大きさ256×256ドットの画像(PBM形式)を作成し、標準出力へ出力する。 なお、実行時にはパイプにて display コマンドを使って表示する。
  2. 画像は左上を原点とし、右に x 軸、下に y 軸をとる。 初期状態では画像全体が白い画像とする。
  3. コマンドライン引数から円の中心座標 x, y の組を1つ以上入力し、 中心 (x, y)、半径 RD (マクロで64と定義する)の円の内部を白黒反転する。 入力された円座標の数だけ白黒反転を行う。
  4. 二次元平面の点については x, y 座標をメンバーにもつ構造体を定義して使用する。
  5. なお、黒丸が実行例1のように画像からはみ出す場合も、うまく表示できるようにすること。
  6. 必要に応じて、さらに関数を作成して良い。

pbm画像の作成については、これまでの演習問題やプログラミング入門のハンドアウト等を参考にせよ。

必要なら数学関数を使用しても良い。使用方法は使用方法はプログラミング入門のハンドアウト等を参照せよ。 その場合、コンパイラのオプションに -lm を与えること。 (コンパイル例: gcc prog04.c -lm

コマンドラインの引数はすべて文字列として得られるが、これを数値(int型) に変換するには、 <stdlib.h>の中の atoi関数 を使用する。 例えば、文字列変数 str に文字列 “123” が入っているとき、 atoi(str) の戻り値は int型の123という値になる。

#include <stdio.h>
#include <stdlib.h>
/* 数学関数を使いたい場合は以下のコメントを外す */
/* #include <math.h> */

/* マクロ定義 */
#define N 256 /* 画像サイズ */
#define RD 64 /* 円盤の半径 */
#define BLACK '1'
#define WHITE '0'

/* 構造体宣言 */
typedef struct{
	int x;
	int y;
}XYdata;

/* 関数のプロトタイプ宣言 */

/* 画像の各ドットを表す外部変数の定義 */
char data[N][N];

int main(int argc, char *argv[]){
	XYdata cent;
	int ncent, i;
	
	/* コマンドラインから円の中心座標を何組か入力される */
	
	/* 入力された中心座標が何組あるか計算 */
	ncent = ???;
	
	/* 画像初期化関数(すべて白くする)をよぶ */
	
	/* 座標の組の数だけ繰り返す */
	
		/* 円の内部を白黒反転する */
		circle(cent);
	
	/* 表示 */
	imgout();
	
	return 0;
}

/* すべての点を白に初期化する関数 */
void init(void){

}

/* 引数で指示された座標の点一つを白黒反転する関数 */
void rev(XYdata a){

}

/* 引数で指示された座標を中心に,半径 RD 以内の点を白黒反転する関数 */
void circle(XYdata c){

}

/* Plain PBM形式で画像データを出力する関数 */
void imgout(void){

}
実行例1: 座標(0, 0)と座標(255, 255)を中心とする円を描く。
% ./a.out 0 0 255 255 | display - &
prog04-3.png

実行例2: 座標(128, 90)と座標(88, 160)と座標(168, 160)を中心とする円を描く。 二つの円が重なる所は白く、三つの円が重なる所は黒くなる。
% ./a.out 128 90 88 160 168 160 | display - &
prog04-4.png

演習問題5(配列をメンバーに含む構造体の配列を利用したプログラム)

ファイル bus-table-u.txtは、 ある年の会津地区から新宿方面行きの高速バスの時刻情報を格納している。 このファイルの 1 行目には 2 行目以降に入っているデータの説明(停留所名など)が書かれており、 2 行目以降に各バス便の号数、運行会社、喜多方?新宿の停留所の時刻が入っている。 時刻は時・分を合わせた3-4桁の整数(11時50分なら1150)で表示されており、 バスが停車しない停留所には値 -999 が入っている。

このとき,以下の説明文を読み、題意を満たすプログラムを作成せよ。
(提出ファイル名 prog05.c)

  1. 時刻データファイルは、コマンドラインで指定されたものを読み込む
  2. データファイルの 1 行目にある、停留所名などを格納する構造体を宣言しておき、 それを使って 1 行目のデータを読み出す。
  3. バス一便ごとのデータを格納する構造体を宣言しておき、 その構造体の配列を使ってファイルから 2 行目以降のデータもすべて読み込む。 なおデータのうち時刻については、4 桁表記のデータを 時・分それぞれ別のメンバーに分けてから構造体に格納する。
  4. 標準入力から停留所名と時間帯(6 時台から 12 時台など。分単位は無視)を入力し、 その停留所に指定した時間帯に停車するバスの時刻情報を表示する(通過する停留所の情報は省略する)。 表示は関数を作成してよび出すことで行う。(時間帯の判定まではmain側で行うとよい)
プログラム例(最初の部分のみ)
#include <stdio.h>
#include <string.h>
#include <stdlib.h>

#define NMAX 20     /* 1日の最大バス本数 */
#define NSTOP 6     /* バス停留所数 */
#define UNDEF -999  /* バスが停車しない時を表すダミー値 */

typedef struct{
	char colstr1[8];        /* カラム1(番号) */
	char colstr2[8];        /* カラム2(会社名) */
	char bsname[NSTOP][12]; /* バスの停留所名 */
}Tableinfo;

typedef struct{
	int number;      /* バス番号 */
	char company[8]; /* 会社名 */
	int hour[NSTOP]; /* 時間 */
	int min[NSTOP];  /* 分 */
}Businfo;

void printInfo(Businfo, Tableinfo); /* 結果表示用関数 */
...
実行例
% ./a.out bus-table-u.txt
Input the name of bus stop.
Wakamatsu
Hours from/to?
7 9

Number: 4 Company: Aizubus
Kitakata      6:50
Wakamatsu     7:30
Oji          11:16
Ikebukuro    11:26
Shinjuku     11:50

Number: 6 Company: JRbus
Wakamatsu     8:30
Inawashiro    9:00
Oji          12:40
Ikebukuro    12:50
Shinjuku     13:14

Number: 8 Company: Aizubus
Wakamatsu     9:30
Oji          13:16
Ikebukuro    13:26
Shinjuku     13:50