新しい型を定義する


typedefキーワードを使えば、データ型の名前を自由に定義することができます。

typedef 既存の型名 新しい型名;

のようにしようします。

typedef int SEISU;

とすれば、int の変わりに SEISU が使えます。

ソースコード

源文件
  1|/* type01.c */
  2|
  3|#include <stdio.h>
  4|
  5|typedef int SEISU;
  6|
  7|SEISU main(void)
  8|{
  9|  SEISU n;
 10|
 11|  for(n = 0; n < 10; n++){
 12|    printf("%d, ", n);
 13|  }
 14|  printf("\n");                                                                       
 15|  return 0;
 16|}

実行結果

0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 


さて、これでは単に int を SEISU に置き換えただけです。

実は、構造体にこの typedef を適用すると、あたかも新しい型ができたのように見えます。実際、新しい型ができたと考えてもよい。


typedef struct _tagData{                                                                  
  int age;
  double bl;
  double bw;
} MYDATA, *LPMYDATA;

とすると、_tagData 型の構造体変数を宣言するとき、

MYDATA mydata;

と書くことができます。また、この構造体へのポインタを宣言するときも、

LPMYDATA lpmydata;

と書くことができます(lpdata に * がついていないことに注意してください。)

typedef キーワードを使えば、データ型の名前を定義できる。

では、サンプルを見てみましょう。

ソースコード

源文件
  1|/* type02.c */
  2|
  3|#include <stdio.h>
  4|#include <stdlib.h>
  5|#include <string.h>
  6|
  7|typedef struct DATA {
  8|  char name[32];
  9|  char tel[32];
 10|} MYDATA, *LPMYDATA;
 11|
 12|int menu();
 13|int meibo_input(LPMYDATA);
 14|int meibo_output(LPMYDATA);
 15|
 16|int main(void)
 17|{
 18|  int selection, loopend = 0;                                                         
 19|
 20|  MYDATA mydata = {"",""};
 21|  LPMYDATA lpMydata = &mydata;
 22|
 23|  while(1){
 24|    selection = menu();
 25|    switch (selection){                                                               
 26|    case 1:
 27|      meibo_input(lpMydata);
 28|      break;
 29|    case 2:
 30|      meibo_output(lpMydata);
 31|      break;
 32|    default:
 33|      loopend = 1;
 34|      break;
 35|    }
 36|    if (loopend)
 37|      break;
 38|  }
 39|  return 0;
 40|}
 41|
 42|int menu()
 43|{
 44|  char ret[8];
 45|                                                                                      
 46|  printf("******* MENU *******\n");
 47|  printf("1:データ入力\n");
 48|  printf("2:データ表示\n");
 49|  printf("0:終了\n");
 50|  printf("********************\n");
 51|  printf("---->");
 52|  scanf("%s", ret);
 53|  return atoi(ret);
 54|}
 55|
 56|int meibo_input(LPMYDATA lpData)
 57|{
 58|  printf("NAME--");
 59|  scanf("%s", lpData->name);
 60|  printf("Tel.No.--");
 61|  scanf("%s,", lpData->tel);
 62|
 63|  return 0;
 64|}
 65|                                                                                      
 66|int meibo_output(LPMYDATA lpData)
 67|{
 68|  if(!strcmp(lpData->name, "\n")){
 69|    printf("データがありません\n");
 70|    return -1;
 71|  }
 72|  printf("Name-- %s\n", lpData->name);
 73|  printf("Tel.No.-- %s\n", lpData->tel);
 74|
 75|  return 0;
 76|}

実行結果

******* MENU *******
1:データ入力
2:データ表示
0:終了
********************
---->1
NAME--  河野太郎
Tel.No.--080-3960-xxxx
******* MENU *******
1:データ入力
2:データ表示
0:終了
********************
---->2
Name-- 河太郎
Tel.No.-- 080-3960-xxxx
******* MENU *******
1:データ入力
2:データ表示
0:終了
********************
---->0

20行目で構造体変数を宣言すると同時に初期化しています。また21行目で、この構造体変数のアドレスを、この構造体へのポインタ lpMydata に代入しています。

そのあと、while の無限ループ突入です。

menu 関数の戻り値を selection に代入し、switch 文で場合分けしています。selection が1ならば、meibo_input 関数を呼び出します。2ならば、meibo_output 関数を呼び出します。その他であれば、loopend 変数に1に代入して switch 文抜けます。

loopend が真(0以外)であれば(36行目)、break文が実行されて while ループを抜けます。もし、「if(loopend)」という書きかたが分かりにくければ、「if(loopend != 0)」としても同じことです。

menu 関数の中で使われている atoi は文字列を int 型の数値に変換する関数です。例えば"123"は文字列ですが、これを atoi 関数で変換すると数値の123を得ることがえきます。menu 関数では、string を int 型の整数に変換して戻り値としています。「atoi」というのは変な名前の関数ですが「Ascii TO Integer」と覚えておきます。

atoi関数

int atoi(
const char *string
         );

atof関数

double atof(
const char *string
            );

もし53行目の「return atoi(ret);」が分かりにくければ、変数 int no; を用意して置いて、

no = atoi(ret);

return no;

と書き換えてみてください。

引数の lpData には mydata 構造体のアドレスが入っています。lpData->name は、name メンバです。

gets(lpData->name);

は、これでよいのでしょうか。gets の引数にはバッファのアドレスを指定します。name は配列の名前なので、これでよいのですね。電話番号(lpData->tel)についても同様です。

構造体からデータを読み出す関数です。特に説明は不要です。

これでは、ちょっとあないですね、何人ものデータを入れられるようにするにはどうしたらよいでしょうか。構造体の配列を使えばいいですね。では、改造しましょう。

ソースコード

源文件
  1|/* type03.c */
  2|
  3|#include <stdio.h>
  4|#include <stdlib.h>
  5|#include <string.h>
  6|
  7|typedef struct DATA {
  8|  char name[32];
  9|  char tel[32];
 10|} MYDATA, *LPMYDATA;
 11|
 12|int menu();
 13|int meibo_input(LPMYDATA);
 14|int meibo_output(LPMYDATA);
 15|
 16|int main(void)
 17|{
 18|  int selection, loopend = 0;
 19|
 20|  MYDATA mydata[10];
 21|  LPMYDATA lpMydata = mydata;
 22|
 23|  memset (mydata, '\0', sizeof(MYDATA[10]));
 24|
 25|  while(1){                                                                           
 26|    selection = menu();
 27|
 28|    switch (selection){
 29|    case 1:
 30|      meibo_input(lpMydata);
 31|      break;
 32|    case 2:
 33|      meibo_output(lpMydata);
 34|      break;
 35|    default:
 36|      loopend = 1;
 37|      break;
 38|    }
 39|    if (loopend)
 40|      break;
 41|  }
 42|  return 0;
 43|}
 44|
 45|int menu()                                                                            
 46|{
 47|  char ret[8];
 48|
 49|  printf("******* MENU *******\n");
 50|  printf("1:データ入力\n");
 51|  printf("2:データ表示\n");
 52|  printf("0:終了\n");
 53|  printf("********************\n");
 54|  printf("---->");
 55|  scanf("%s", ret);                                                                   
 56|  return atoi(ret);
 57|}
 58|
 59|int meibo_input(LPMYDATA lpData)
 60|{
 61|  char strno[8];
 62|  int no;
 63|
 64|  printf("何番のデータを入力しますか(0-9)--");
 65|    scanf("%s", strno);
 66|    if (strno[0] < '0' || strno[0] > '9'){
 67|      printf("入力が不正です\n");
 68|      return -1;
 69|    }
 70|    strno[1] = '\0';
 71|    no = atoi(strno);
 72|
 73|  printf("NAME--");
 74|  scanf("%s", lpData->name);
 75|  printf("Tel.No.--");                                                                
 76|  scanf("%s,", lpData->tel);
 77|
 78|  return 0;
 79|}
 80|
 81|int meibo_output(LPMYDATA lpData)
 82|{                                                                                     
 83|  char strno[8];
 84|  int no;
 85|
 86|  printf("何番のデータを読み出しますか(0-9)--");
 87|  scanf("%s", strno);
 88|  if (strno[0] < '0' || strno[0] > '9'){
 89|    printf("入力が不正です\n");
 90|    return -1;
 91|  }
 92|  strno[1] = '\0';
 93|  no = atoi(strno);
 94|
 95|  if (!strcmp( (lpData + no)->name, "\n") ){
 96|    printf("データがありません\n");
 97|    return -1;
 98|  }
 99|
100|  printf("Name-- %s\n", lpData->name);
101|  printf("Tel.No.-- %s\n", lpData->tel);
102|
103|  class="keyword">return 0;
104|}         

実行結果

20〜21行目の

MYDATA mydata[10];

LPMYDATA lpMydata = mydata;

を何をしているのでしょうか。mydataは配列の名前ですね。配列の名前は先頭要素のアドレスでした。したがって21行目は

LPMYDATA lpMydata = &mydata[0];

と同じことです。DATA型構造体へのポインタlpMydataに、mydata[0]のアドレスを代入しているわけです。

23行目の memset は見慣れない関数ですね。でも、メモリ(mem)に何かをセットする関数ではないかな、と想像できませんか。実はその通りなのです。

void *memset(                                                                             
             void *dest,
             int c,
             size_t count
             );

この関数は指定したいパッファ(dest)を、指定した文字(c)count 個で埋め尽くす関数なのです。なお、この関数を使うときは string.h をインクルードする必要があります。

ここでは、構造体メンバをすべてヌル文字で埋め尽くしています。なぜこのような処理が必要かというと、データ読み出しのとき、データの収められていない構造体配列の要素を指定したときに「データがありません」と表示するときの判断材料にしているからです。

meibo_input 関数を見てみましょう。

「何番のデータを入力しますか(0-9)--」と表示した後、ユーザーが入力した文字列をgets関数で取得して strno に格納します。データの番号とは配列添字のことです。

次に strno の最初の1文字を調べます。これが'0'から'9'の範囲に入っていなければ「入力が不正です」と表示して return します。

次に strno[1] に '\0' を代入しています。たとえばユーザーが「112」と入力した場合でも、strno[1]にヌル文字を代入すればこの文字列は"1"と見なされますね。あとは、この文字列を整数値に変換して no に代入します。

さて、引数の lpData は main 関数の mydata[0] のアドレスです。mydata[n]のアドレスは(mydata + n)ですね。したがって mydata[n] の name メンバは

(mydata + n)->name

となります。telメンバについても同様です。

Chapte8 @ C言語目録 @ HomeWork List @ 昭亮's Homepage