2023 年度「計算数学b」 2023-07-21

構造体

#include <stdio.h>

struct complex {  /* struct complex 型の構造体の定義 */
  double re;
  double im;
};                /* このセミコロンを忘れやすい! */

struct complex new(double re, double im);
struct complex add(struct complex z, struct complex w);
int isequal(struct complex z1, struct complex z2);
void print(struct complex z);

int main() {
  struct complex z, w;    /* struct complex 型変数の宣言 */

  z.re = 1; z.im = 2;     /* ひとつの計算例 */
  w.re = 1; w.im = 3;

  print(z);
  printf(" + ");
  print(w);
  printf(" = ");
  print(add(z, w));
  printf(".\n");

  if (isequal(z, w) == 1) {     /* 別の例 */
    printf("等しい.\n");
  } else {
    printf("等しくない.\n");
  }

  print(add(new(-1, 3), new(-3, 4)));   /* こんな書き方もできる */
} 


/* 実部 re, 虚部 im の struct complex 型変数を返す */
struct complex new(double re, double im) {
  struct complex r;

  r.re = re; r.im = im;

  return r;
}


/* 和を返す */
struct complex add(struct complex z, struct complex w) {
  struct complex r;

  r.re = z.re + w.re;
  r.im = z.im + w.im;

  return r;
}


/* 等しければ 1 を、そうでなければ 0 を返す */
int isequal(struct complex z1, struct complex z2) {
  if (z1.re == z2.re && z1.im == z2.im) {
    return 1;
  } else {
    return 0;
  }
}


/* 画面出力 */
void print(struct complex z) {
  printf("(%f + %f i)", z.re, z.im);
}

「構造体」という名前はいかめしいが,英語では structure である。 ひとつの変数に複数の要素がある場合に用いる。 たとえば,一つの複素数が実部と虚部からなるように。

「struct complex 型の構造体の定義」の部分。 struct は必ずこう書く。 次の complex は,“複素数型”を作りたいので, 複素数 complex number にちなんでこの名前を選んだ。 こうすると「struct complex」が int や double と同じように使える。

この構造体は,実部 re と虚部 im, 二つの double 型変数からなる。 閉じ中カッコのあとにセミコロンを忘れないように。 いままでは閉じ中カッコのあとにセミコロンはつかないのだった。

これで,main() 関数の中の「struct complex 型変数の宣言」が理解できたであろう。 z や w はこの型の変数である。

(ここで注意。構造体の定義と,その構造体型の変数の宣言とを混同してはならない。 前者は,これから,そういう型を使いますよ,というもの, 後者はその型の変数の宣言である。 前者を,ベルギーワッフルの型を作ったのだと思うと理解しやすいかもしれない。 後者は,その型で焼いたベルギーワッフルである。 この比喩はとある本から借用した。)

構造体の成分を利用するには,z.re のようにする。 これで struct complex 型の変数 z の re 成分を意味する。 これで、関数 add() と print() はわかったであろう。 関数 isequal() は、if (z1 == z2) と書けないので、 その代わりをさせるために作ったものである。

関数 new() は、実部と虚部の値を与えると struct complex 型変数を返すもので、 名前は C++ 言語を(不器用に)まねてみたものである。

興味のあるものは、引き算 sub(), 掛け算 mul(), 割り算 div() などを実装してみよ。

変数 z, w1, w2が struct complex 型だとして、 z = w1 + w2; のように書けたら便利だが、そうは書けない。 struct complex 型の変数の間の加法は定義されていないからである。 C 言語のあとから開発された C++ 言語ではこれができるそうである。

また、複素数体型は、最近の C コンパイラにはついているようである。

小規模なプログラムでは、構造体を定義せず、 are, bre で a, b の実部、 aim, bim で a, b の虚部、として書いたほうが楽かもしれない。 大規模なプログラムになると、まず構造体を定義し、 その構造体に関連する関数 (複素数型の場合は画面出力、加減乗除など) を実装してから始めるほうが効率がよいと聞く。 一度きちんと作った構造体のプログラムは、あとで使いまわしがきく。

発展練習――有理数型

有理数型 struct rational を実装してみよ。 英語では、分子は numerator, 分母は denominator という。 成分名は num, denom でどうだろうか。 演算結果は既約分数にしよう。

a/b + c/d = (ad + bc) / (bd) でもよいが、こう計算してから約分するのは、 1/65536 + 1/65536 ですでに破綻する。 65536 * 65536 は大きすぎて int 型に格納できないからである。 小学校で習ったように、最小公倍数で通分するか。いろいろ試してみよ。

(65536/177147) * (177147/65536) を、 分母・分子それぞれ掛けてから約分しようとすると int 型の限界を超える。 前もって“約分”しておくか。いろいろ試してみよ。

1/3 + 1/4 = 7/12 のように出力する、“有理数電卓”を作ってみては?


岩瀬順一