第7回目 構造体とアクセサ(5/22)

今日のキーワード

■ 学籍番号と名前の構造体

学籍番号と名前を格納する構造体 Student は、次のように宣言する。
#define MAX_GAKUSEKI 10
#define MAX_NAME 20
typedef struct{
    char gakuseki[MAX_GAKUSEKI];
    char name[MAX_NAME];
} Student;
学籍番号を代入する関数を set_gakuseki、名前を代入する関数を set_name とする。これらは、構造体の内部の変数(メンバ変数という)を読み書きする関数なので、アクセサと呼ばれる。set_gakuseki は以下のように定義する。
int set_gakuseki(Student *s,const char in[])
{
  int i;
  for( i=0 ; i < MAX_GAKUSEKI ; i++ ){
    s−>gakuseki[i]=in[i];
    if( in[i]==’\0’ ){
      return( 1 );
    }
  }
  s−>gakuseki[i−1] = ’\0’;
  return( 0 );
}
MAX_GAKUSEKI と MAX_NAME は、学籍番号と名前の文字列を格納するためのサイズである。具体的に 10 や 20 という値が、コンパイル時にあてはめられる。このような数値は、プログラムの中に直接書くと、後から値の変更が困難になると同時に、プログラムの読み手に意図が伝わらない(マジックナンバーと呼ばれる)。
関数set_gakusekiでは、引数の文字列が MAX_GAKUSEKI を越えた場合について対処している。越えた場合は 0 を返し、正しく代入できなかったことを呼出し元に伝えている。
また、メイン関数は以下のようになる。
main()
{
  Student *s1;

  s1 = (Student *)malloc(sizeof(Student));
  set_gakuseki( s1, "982001" );
  set_name( s1, "青野直樹" );

  print_student( s1 );
}
エラーチェックを含めると以下のようになる。
main()
{
  Student *s1;

  s1 = (Student *)malloc(sizeof(Student));
  if( s1 == NULL ){
    fprintf(stderr,"error in malloc\n");
    exit(-1);
  }
  if( set_gakuseki( s1, "982001" ) == 0 ){
    fprintf(stderr,"error in set_gakuseki\n");
    exit(-1);
  }
  if( set_name( s1, "青野直樹" ) == 0 ){
    fprintf(stderr,"error in set_name\n");
    exit(-1);
  }
  print_student( s1 );
  free(s1);
}

□ 演習1

上記のプログラムを参考に、名前を代入する関数 set_name を作成せよ。
学籍番号および名前を表示する関数 print_student を作成せよ。(ヒント void print_student( Student *s ) )なお、print_student もアクセサである。⇒ prac0701.c

□ 演習2

生年月日を int year, int month, int day として、構造体 Student を拡張せよ。
また、それらに代入するアクセサ int set_birthday(Student *s, const char yyyymmdd[]) を作成せよ。yyyymmdd は char の配列変数の名前であり、1980年1月31日産まれの場合は "19800131" となっているものとする。半角数字のみ文字列に含まれていると仮定してよい。
print_student も生年月日が表示されるように修正すること。⇒ prac0702.c

■ 標準入力の使い方

stdin は標準入力である。コマンドライン上から、stdin を使ったプログラムを単に実行すると、標準入力とは、キーボードからの入力になる。しかし、unix では、もっと高度な使い方がある。
# ./a.out < fileX
このように実行すると、a.out のプログラムで、stdin から入力するタインミングで、ファイル fileX の内容が入力される。
また、繰り返し文の中で fgets を用いるには定石がある。
/* list0701.c */
#include <stdio.h>
main()
{
  char buff[BUFSIZ];
  while( fgets(buff,BUFSIZ,stdin)!=NULL){
    printf(”%s”,buff);
  }
}
list0701.c を入力しコンパイルした後、
# ./a.out < list0701.c
を実行してみよう。

■ 宿題

以下の課題について、手書きコメント付きソースプログラム、および、実行結果を提出せよ。
  1. 1人の情報を格納している名簿のファイルを次のように作る。⇒ data.txt
    1行目に学籍番号、2行目に氏名、3行目に英語氏名、4行目に住所、5行目に演習2で示した形式の生年月日、6行目にメールアドレスを記入する。
    022001
    阿部泰洋
    Yasuhiro Abe
    鳥取県鳥取市湖山町南5丁目
    19840302
    abe@ike.tottori-u.ac.jp
    
  2. 上記のデータを格納するように、構造体 Student を拡張せよ。なお、英語氏名は char ename[MAX_ENAME]; 住所は char address[MAX_ADDRESS]; メールアドレスは char email[MAX_EMAIL]; とせよ。各定数を適切に define せよ。
  3. 追加したメンバ変数に代入をするアクセサ set_ename, set_address, set_email を作成せよ。
  4. ファイルから情報を読み込み、上記で作成した構造体に格納し、表示するプログラムを作成せよ。なお、この宿題では、list0701.c のように while 文は使用せず、fgets を並べる。⇒ prac0703.c
  5. (それから、そろそろ気づいたと思うが、string 系のライブラリを使用しても構わない。また、fgets で得られる文字列の末尾から改行コードを取り除く関数 myfgets を別途作成してもよい。その他、工夫した事があれば、「考察」という章立てをして自由に記述すること。)
    実行の様子
    # gcc prac0703.c
    # ./a.out < data.txt
    学籍番号: 022001
    名  前  : 阿部泰洋(Yasuhiro Abe)
    生年月日: 1984年3月2日
    住  所  : 鳥取県鳥取市湖山町南5丁目
    E-Mail  : abe@ike.tottori-u.ac.jp
    

(c) Masato TOKUHISA, 2003, May. 19