C言語の解説と演習

(第11回)

メニュー

「N88BASICについて」へ行く

「C言語の解説と演習(第十回)」へ行

トップページへ行く

質問箱へ行く



はじめに

これまでいろいろとC言語の文法について学んできました.今回で文法に関する解説は最後にしようと思います.C言語には、結構細かい規則がいろいろとありますので、これまで解説してきたことだけで十分であるとはとても言えませんが、あと「構造体」をやれば、必要最低限のことはやってきたと思います.
というわけで、今回はC言語の最後の関門である「構造体」を解説します.


構造体とは?

「構造体」もポインタと同様、BASIC には無いものです.このため、ここでつまづく人も結構いるようですが、分かってしまえば決して難しいものではありません.むしろ非常に自然なものであり、うまく活用すればプログラムを非常に判りやすいものにしてくれます.
「構造体」とは一言で言うと、いろいろなデータをまとめて1つにした型です.こう言っても分かりにくいでしょうから、例で示しましょう.
今、実部と虚部がともに整数である複素数を扱いたいとします.このとき、


      int re,im;

と実部と虚部を別々に宣言しても良いのですが、re と im は1つの数値の実部と虚部ですから、まとめて扱いたいものです.そこで構造体を用いて


      struct complex {
         int re,im;
      };

と構造体(struct) complex が、整数型 の変数 re, im をもつと定義します.そうすると 、struct complex は整数型 の変数 re, im を一緒にまとめた1つの型となります.そして

      
      struct complex a;

と宣言すると、整数型の変数


      a.re, a.im 

がプログラムの中で使えるようになります.例えば、複素数 a = 1+2 i , b = -1 + i の和 a+b を複素数 c へ代入するプログラムは、次のようになります.


      struct complex a, b, c;

      a.re = 1;
      a.im = 2;
      b.re = -1;
      b.im = 1;
      c.re = a.re + b.re;
      c.im = a.im + b.im;

上のプログラムを見て、配列を使って、次のようにしても全く同じことができると思った人がいるかもしれません.


      int a[2], b[2], c[2];

      a[0] = 1;
      a[1] = 2;
      b[0] = -1;
      b[1] = 1;
      c[0] = a[0] + b[0];
      c[1] = a[1] + b[1];

上のプログラムでは、配列の始めの要素に実部を、その次の要素に虚部を当てています.このように構造体は複数のデータを一緒にして扱うという点では、配列と似たところがあります.ただし、構造体の場合は違った型のデータを1つにまとめることができます.例えば次のような構造体を定義することも可能です.


      struct seiseki {
      int kokugo, shakai, sugaku, rika, eigo;
      char name[30];
      };

上の構造体では、学生の国語、社会、数学、理科、英語の成績( int kokugo, shakai, sugaku, rika, eigo)とその学生の名前( char name[30] ) を1つの構造体 seiseki の中に定義しています.ここで char name[30] とは、文字型 char の配列で文字列(例えば「北本卓也」のような)を代入する変数を表しています.こうすれば

      
      struct seiseki seito1;

      seito1.kokugo = 80;
      seito1.shakai = 70;
      seito1.sugaku = 85;
      seito1.rika = 75;
      seito1.eigo = 80;
      strcpy(seito1.name,"Kitamoto Takuya");

と言った感じで生徒一人の成績を、1つの変数 seito1 で扱うことができます.ちなみに strcpy(seito1.name,"Kitamoto Takuya") は文字列 "Kitamoto Takuya" を、seito1.name にコピーする関数です.上のプログラムを実行したとき、メモリの内容は次のようになっています.



構造体を使ってみよう

それでは構造体を使って、複素数の和、差、積を計算するプログラムを作ってみましょう.前回の解説で整数の和、差、積を計算するプログラムを紹介しましたが、それを少し修正するだけです.プログラムは以下のようになります.


#include <stdio.h>

struct complex {
  int re, im;
};

main()
{

  struct complex x,y,wa,sa,seki;
  void tashizan(struct complex x, struct complex y, 
                struct complex *wa);
  void hikizan(struct complex x, struct complex y, 
               struct complex *sa);
  void kakezan(struct complex x, struct complex y, 
               struct complex *seki);

  printf("x.re=");
  scanf("%d",&(x.re));
  printf("x.im=");
  scanf("%d",&(x.im));
  printf("y.re=");
  scanf("%d",&(y.re));
  printf("y.im=");
  scanf("%d",&(y.im));

  tashizan(x,y,&wa);
  hikizan(x,y,&sa);
  kakezan(x,y,&seki);

  printf("x+y=%d+%d*i ¥n",wa.re,wa.im);
  printf("x-y=%d+%d*i ¥n",sa.re,sa.im);
  printf("x*y=%d+%d*i ¥n",seki.re,seki.im);

}

void tashizan(struct complex x, struct complex y, 
              struct complex *kekka) {

  (*kekka).re = x.re+y.re;
  (*kekka).im = x.im+y.im;
}

void hikizan(struct complex x, struct complex y, 
             struct complex *kekka) {

  (*kekka).re = x.re-y.re;
  (*kekka).im = x.im-y.im;
}

void kakezan(struct complex x, struct complex y, 
             struct complex *kekka) {

  (*kekka).re = x.re*y.re-x.im*y.im;
  (*kekka).im = x.re*y.im+x.im*y.re;  
}


上のプログラムの前回のプログラムからの変更点は、以下の通りです.

  1. 前回のプログラムで int だった変数 x, y, wa, sa, seki を、struct complex に変えた.
  2. 関数 tashizan, hikizan, kakezan は何も値を返さない void 型とした.
  3. 関数 tashizan, hikizan, kakezan で行われる計算を
    Re(x+y) = Re(x)+Re(y); Im(x+y) = Im(x)+Im(y)
    Re(x-y) = Re(x)-Re(y); Im(x-y) = Im(x)-Im(y)
    Re(x*y) = Re(x)*Re(y)-Im(x)*Im(y); Im(x*y) = Re(x)*Im(y)+Im(x)*Re(y)
    の規則に従い、書き換えた.

プログラムを実行させると、x.re, x.im, y.re, y.im の値を聞いてきますから、それぞれ -2,3,1,-2 と入力すると、

  x+y=-1+1*i
  x-y=-3+5*i
  x*y=4+7*i
と表示されます.


最後に一言

実は上のプログラムで

   (*kekka).re
のところは、

   kekka->re
と書くことができます.同様にして

   (*kekka).im
のところは

   kekka->im
と書くことができます.これらは良く使われる記法なので覚えておいてください.この記法を用いて上のプログラムを 書きなおすと、次のようになります.

#include <stdio.h>

struct complex {
  int re, im;
};

main()
{

  struct complex x,y,wa,sa,seki;
  void tashizan(struct complex x, struct complex y, 
                struct complex *wa);
  void hikizan(struct complex x, struct complex y, 
               struct complex *sa);
  void kakezan(struct complex x, struct complex y, 
               struct complex *seki);

  printf("x.re=");
  scanf("%d",&(x.re));
  printf("x.im=");
  scanf("%d",&(x.im));
  printf("y.re=");
  scanf("%d",&(y.re));
  printf("y.im=");
  scanf("%d",&(y.im));

  tashizan(x,y,&wa);
  hikizan(x,y,&sa);
  kakezan(x,y,&seki);

  printf("x+y=%d+%d*i ¥n",wa.re,wa.im);
  printf("x-y=%d+%d*i ¥n",sa.re,sa.im);
  printf("x*y=%d+%d*i ¥n",seki.re,seki.im);

}

void tashizan(struct complex x, struct complex y, 
              struct complex *kekka) {

  kekka->re = x.re+y.re;
  kekka->im = x.im+y.im;
}

void hikizan(struct complex x, struct complex y, 
             struct complex *kekka) {

  kekka->re = x.re-y.re;
  kekka->im = x.im-y.im;
}

void kakezan(struct complex x, struct complex y, 
             struct complex *kekka) {

  kekka->re = x.re*y.re-x.im*y.im;
  kekka->im = x.re*y.im+x.im*y.re;  
}


それでは今回のまとめを行います.今回もいろいろと新しいことが出てきました.
  1. 「構造体」とは一言で言うと、いろいろなデータをまとめて1つにした型である.
  2. 配列も複数の変数を1つ変数にまとめるものだが、構造体の場合は違った型の変数を1つにまとめることができ。
  3. 構造体の定義は次の形を取る.
    
            struct 構造体名 {
    
                構造体の中の変数名;
           
            };
    
  4. 構造体 a を
    
                struct 構造体名 a;
    
    と宣言すると、構造体の中の変数が
    
                a.変数名
    
    の形で使える.
  5. 上のプログラム例で示したように、
    
       (*kekka).re
    
    のところは、
    
       kekka->re
    
    と書くことができる.

次回の予定

始めに述べたように今回で文法編は終わりです.これまでご苦労様でした.

演習問題

これまで学習したことを用いて、C言語のプログラムを作り、kitamoto@po.cc.yamaguchi-u.ac.jp にメールで送れ.プログラムの内容は何でも良い.