内容

住所録プログラムの作成 (その1


住所録の概要

以下のような仕様の住所録を作成する。


データ構造

学籍番号、氏名、ふりがな(ローマ字読み)、年齢、生年月日、住所等を構造体を用いて作成する。

プログラムの仕様


データ構造の作成

データは下のような構造体メンバーで構造体personalを作成する。

 

struct personal{
  int no;
  char name[256];
  char furigana[256];
  int age;
  int year;
  int month;
  int day;
  char address[256];
}
;

 


メニューの作成

データの入力や表示、ファイル入出力を行うため、メニューを作成し、ユーザーが行いたいものを選択できるようにする。

メニューはprint_menu関数により表示し、scanfによりユーザーからの入力を得る。入力された番号をswitch文を用いて、それぞれの仕事を行う関数を呼び出すことにする。

print_menu関数は以下のようにする。

 

void print_menu()
{
  printf("\n");
  printf("1)
データ入力\n");
  printf("2) データ出力\n");
  printf("3) ファイル読み込み\n");
  printf("4) ファイルに保存\n");
  printf("5) 終了\n\n");
  printf("番号をいれて下さい : ");
}

 

main関数はユーザーからの入力に合わせて、それぞれの仕事に対応した関数を呼び出すため、変数の宣言とswitch文からなる。

 

main()
{
  int no, number = 0;
  struct personal data[100];

  while(1){
    print_menu();
    scanf("%d", &no);

    switch(no){
    case(1):
    get_data(data, number);
    number++;
    break;

    case(2):
    print_data(data, number);
    break;

(以下、メニューの数だけ続く)


データの入出力

データの入出力はそれぞれの関数を作成する。主に入力はscanf文、出力はprintf文からなる。

 

void get_data(struct personal data[], int number)
{
  printf("学籍番号 : ");
  scanf("%d", &data[number].no);
  printf("氏名 : ");
  scanf("%s", data[number].name);
  printf("ふりがな : ");
  scanf("%s", data[number].furigana);
  printf("年齢 : ");
  scanf("%d", &data[number].age);
  printf("生年月日 (): ");
  scanf("%d", &data[number].year);
  printf("生年月日 (): ");
  scanf("%d", &data[number].month);
  printf("生年月日 (): ");
  scanf("%d", &data[number].day);
  printf("住所 : ");
  scanf("%s", data[number].address);
}

void print_data(struct personal data[], int number)
{
int x;

  for(x = 0; x < number; x++){
    printf("\n%d ------------------------------\n", x);
    printf("%d\n", data[x].no);
    printf("%s\n", data[x].name);
    printf("%s\n", data[x].furigana);
    printf("%d\n", data[x].age);
    printf("%d %2d %2d日生まれ\n", data[x].year, data[x].month, data[x].day);
    printf("%s\n", data[x].address);
  }
}

 


ファイルの入出力

ファイルの入出力もデータの入出力と同様に実現できる。

ここでも、ファイル入出力用の関数を作成し、メニューで選択されたらその関数を呼び出すこととする。

ファイルのデータ書式は、先頭にデータ数、その後テキストで構造体のメンバーを順に羅列したものとする。

メニューによる呼び出しとファイル入出力関数は以下のように実現できる。

 

main()
{
  int no, number = 0, flag;
  struct personal data[100];

  while(1){
    print_menu();
    scanf("%d", &no);

    switch(no){

(省略)

    case(3):
      flag = file_load(data, &number);
      if(flag == 1){
        printf("
ファイルが見つかりません\n");
      }
      break;


    case(4):
      flag = file_save(data, number);
      if(flag == 1){
        printf("
ファイルに書き込めません\n");
      }
      break;

  }

(省略)

}

int file_load(struct personal data[], int *number)
{
  FILE *fp;
  char filename[256];
  int i;

  printf("
ファイル名を入力してください:");
  scanf("%s", filename);

  fp = fopen(filename, "r");


  if(fp == NULL){
    return(1);
  }

  fscanf(fp, "%d", number);

  for(i = 0; i < *number; i++){
    fscanf(fp, "%d", &data[i].no);

    fscanf(fp, "%s", data[i].name);
    fscanf(fp, "%s", data[i].furigana);
    fscanf(fp, "%d", &data[i].age);
    fscanf(fp, "%d", &data[i].year);
    fscanf(fp, "%d", &data[i].month);
    fscanf(fp, "%d", &data[i].day);
    fscanf(fp, "%s", data[i].address);
  }

  fclose(fp);

  return(0);
}

int file_save(struct personal data[], int number)
{
  FILE *fp;
  char filename[256];
  int i;


  printf("ファイル名を入力してください:");
  scanf("%s", filename);

  fp = fopen(filename, "w");


  if(fp == NULL){
    return(1);
  }

  fprintf(fp, "%d\n", number);

  for(i = 0; i < number; i++){

    fprintf(fp, "%d\n", data[i].no);
    fprintf(fp, "%s\n", data[i].name);
    fprintf(fp, "%s\n", data[i].furigana);
    fprintf(fp, "%d\n", data[i].age);
    fprintf(fp, "%d\n", data[i].year);
    fprintf(fp, "%d\n", data[i].month);
    fprintf(fp, "%d\n", data[i].day);
    fprintf(fp, "%s\n", data[i].address);

  }

  fclose(fp);
  return(0);
}

 


動的メモリ確保

メモリを確保する方法には次の二つの方法がある。

もし、住所録のデータを記憶するために配列を用いると宣言した配列の大きさ以上のデータを扱えなくなり、多くのデータが必要な時にはもう一度プログラムを書き換えてコンパイルし直さなくてはならない。それに対して動的にメモリを確保すれば、扱うデータ数が増えても必要なだけ記憶のためのメモリを確保できるので便利である。

メモリの動的な確保、解放を行うには次の関数を用いる。これらの関数の定義はstdlib.hにて定義されているので、これをインクルードする必要がる。

 

void *malloc(size_t size)

void free(void *ptr)

 

malloc関数はsizeで指定されたバイト数のメモリを確保し、確保したメモリへのポインタを返す。通常、代入したい変数の型へのキャストを行う。

free関数はポインタptrで示されたメモリ領域を解放する。

確保したいメモリの型(構造体)のバイト数はsiteof演算子を用いることによって知ることができる。例えば

 

printf("sizeof(double)= %d\n", sizeof(double));

printf("sizeof(FILE) = %d\n", sizeof(FILE));

 

とすれば、doubleFILEそれぞれのバイト数が 8 バイト、 16 バイトであることが分かる。自分で作った構造体についても同様にして調べることができる。

動的に確保されたメモリはfree関数を用いて開放するか、プログラムが終了するまで確保されつづける。したがって、不要になったメモリは必ず解放すること

以下はmalloc関数を用いてint型の100個の配列を獲得するサンプルである。ここで獲得された配列は以降通常の配列と同様に使用することができる。

 

#include<stdio.h>
#include<stdlib.h>

main()
{
  int *array;

  /*
メモリの獲得 */
  array = (int*)malloc(sizeof(int)*100);

  free(array);
}

 

構造体の配列を動的に獲得する方法も同様にして実現できる。

 

#include<stdio.h>
#include<stdlib.h>

struct personal{
  int no;
  char name[256];
  char furigana[256];
  int age;
  int year;
  int month;
  int day;
  char address[256];
}
;

main()
{
  struct personal *array;

  /*
メモリの獲得 */
  array = (struct personal *)malloc(sizeof(struct personal) * 100);

  free(array);
}