8.構造体

Copyright(C) 14Mar2003 coskx

構造体を用いると複数の数値や文字列で1つの概念を形成するものの記述が楽になる。
ここで修得してほしい内容は以下の通りである。
1.複数の値で1つの概念を形成するものは構造体を導入すると表現が自然になることを理解する。
2.新しい型の定義により,構造体を利用可能にする。
3.構造体の変数の任意のメンバに対する値の設定や参照が自由に出来る
4.構造体がデータベースに用いられる例を理解する
5.複素数の構造体を例に取り,有用な関数群を整備する手法を理解する。


 8.1 複数の値で1つの概念を形成するもの(構造体を使わなかった場合)

8.1.1 座標を表す
平面内の点の座標をプログラムで表現することを考えよう。点の座標は(42,86)のようにx座標とy座標とで表される。単純に考えるために,x座標とy座標ともに整数値しかとらないものとする。
(1)1つの点のみを考える時は,x座標とy座標それぞれにint型変数を割り当てて,
  int x; /*x座標*/
  int y; /*y座標*/
  x=42; y=86;
または
 int x=42; /*x座標*/
  int y=86; /*y座標*/
とすれば点(42,86)を表したことになるであろう。
(2)数個の点を扱うときでも
 int x1=42,y1=86, x2=27,y2=40, x3=12,y3=47;
とすれば点(42,86),(27,40),(12,47)の3点を表したことになるであろう。
(3)100個くらいの点を扱う時は,配列を用いることになるであろう。
 int x[100],y[100];
として,ファイルから点の座標を読み込んだり,プログラム中で座標を生成したりするであろう。
  x[82]=47;y[82]=25;
というのは点番号82のx座標は47で,y座標は25であるという意味である。このままでもプログラムの見通しが悪いわけではない。
 x[34]=x[82];y[34]=y[82];
というのは,点番号82の点の座標を点番号34にコピーしたことになる。
 int x[3]={42,27,12};
  int y[3]={86,40,47};
というのは点(42,86),(27,40),(12,47)の3点を表したつもりである。ある点の座標が,2箇所にばらばらに記述されることになる。
点の数が100点もあると,見通しが悪い。

8.1.2 分数を表す
分数をプログラムで表現することを考えよう。分数は 42/86 のように分子と分母とで表される。(約分の必要性は別に考える)
(1)1つの分数のみを考える時は,分子と分母それぞれにint型変数を割り当てて,
  int x; /*分子*/
  int y; /*分母*/
  x=42; y=86;
または
 int x=42; /*分子*/
  int y=86; /*y分母*/
とすれば分数 42/86 を表したことになるであろう。
(2)数個の分数を扱うときでも
 int x1=42,y1=86, x2=27,y2=40, x3=12,y3=47;
とすれば分数 42/86, 27/40, 12/47 の3つの分数を表したことになるであろう。
(3)100個くらいの分数を扱う時は,配列を用いることになるであろう。
 int x[100],y[100];
として,ファイルから分数を読み込んだり,プログラム中で分数を生成したりするであろう。
  x[82]=47;y[82]=25;
というのは分数番号82の分子は47で,分母は25であるという意味である。このままでもプログラムの見通しが悪いわけではない。
 x[34]=x[82];y[34]=y[82];
というのは,分数番号82の分数を分数番号34にコピーしたことになる。
 int x[3]={42,27,12};
  int y[3]={86,40,47};
というのは分数 42/86, 27/40, 12/47 の3つの分数を表したつもりである。ある分数の分子分母が,2箇所にばらばらに記述されることになる。
分数の数が100個もあると,見通しが悪い。


 8.2 構造体の導入


8.2.1 座標を表す構造体

1つの座標を1つの変数で表す方法をC言語は提供することができる。

(1)構造体の導入

座標を表す変数pntがあり,内部には2つの部屋があり,部屋の名前を x と y とする。
変数pntの部屋xに42を,変数pntの部屋yに86を代入し,これを点pntのx座標に42,y座標に86を設定したことにしたい。
実際には
  pnt.x=42; pnt.y=86;
のように記述したい。

このように1つの変数内に複数の部屋がある変数は構造体と呼ばれる。構造体を使うには次のような2段階がある。

第1段階 内部に2部屋あり部屋の名前がx,yである新しい変数型を作る (変数の型の名前をpoint_tとする)
第2段階 point_t型の変数pntを宣言する

具体的には

/*point_t型の定義*/
typedef struct {
    int x; /*x座標の部屋*/
    int y; /*y座標の部屋*/
} point_t;

/*point_t型の変数の宣言*/
    point_t pnt;

のように記述する。このような宣言がなされているもとで
    pnt.x=42; pnt.y=86;
ができるようになる。

List 8.2.1 構造体の紹介プログラム
#include <stdio.h>

/*point_t型の定義*/
typedef struct {
    int x; /*x座標の部屋*/
    int y; /*y座標の部屋*/
} point_t;

int main()
{
    point_t pnt; /*point_t型の変数の宣言*/
    pnt.x=42;
    pnt.y=86;
    printf("pnt.x pnt.y = %d %d\n", pnt.x, pnt.y);
    return 0;
}

実行結果
pnt.x pnt.y = 42 86


point_t は型の名前であるが,これまでの int double char なども型の名前であった。
変数を宣言するときには,
  型の名前 変数の名前;
のように書くことになっているので,
  int hensuu1;
と同じように
  point_t pnt;
と書いている。


(2)構造体宣言時の初期値代入

変数宣言時の初期化代入をするなら
    point_t pnt={42,86};
のように書くことができる。

この型の変数が数個あり,変数宣言時の初期化代入をするなら
    point_t p1={42,86}, p2={27,40}, p3={12,47};
のように記述し,点(42,86),(27,40),(12,47)の3点を表すことができる。

List 8.2.2 構造体の紹介プログラム
#include <stdio.h>

/*point_t型の定義*/
typedef struct {
    int x; /*x座標の部屋*/
    int y; /*y座標の部屋*/
} point_t;

int main()
{
    point_t p1={42,86}, p2={27,40}, p3={12,47};
    printf("p1.x p1.y = %d %d\n", p1.x, p1.y);
    printf("p2.x p2.y = %d %d\n", p2.x, p2.y);
    printf("p3.x p3.y = %d %d\n", p3.x, p3.y);
    return 0;
}

実行結果
p1.x p1.y = 42 86
p2.x p2.y = 27 40
p3.x p3.y = 12 47


(3)構造体の配列

この型の変数の配列を作ったら
    point_t point[100];
である。構造体はint型変数2つの組であり,それが100組あるので,int型の値は200個保存できる。

    point[82].x=47;point[82].y=25;
というのは点番号82のx座標は47で,y座標は25であるという意味である。この表記はあまり「ごりやく」が感じられない。

しかし
    point[34]=point[82];
というのは,点番号82の点の座標を点番号34にコピーしたことになる。
(配列では内部の部屋を1つずつコピーする必要があったが,構造体では構造体を代入することで内部の部屋ごとにコピーが完了する)
これは便利であろう。

変数宣言時の初期化代入をするなら
    point_t mypoint[3]={{42,86},{27,40},{12,47}};
というのは点(42,86),(27,40),(12,47)の3点を表している。
すこし,見通しがよくなったと感じられる。点の数が100点あっても,ある点の座標が,前の例のように2箇所にばらばらに記述されることはない。

List 8.2.3 構造体の紹介プログラム
#include <stdio.h>

/*point_t型の定義*/
typedef struct {
    int x; /*x座標の部屋*/
    int y; /*y座標の部屋*/
} point_t;

int main()
{
    point_t point[100];
    point_t mypoint[3]={
        {42,86},{27,40},{12,47}
    };
    int i;

    point[82].x=47;
    point[82].y=25;
    printf("point[82].x point[82].y = %d %d\n", point[82].x, point[82].y);

    point[34]=point[82];
    printf("point[34].x point[34].y = %d %d\n", point[34].x, point[34].y);

    for (i=0; i<3; i++) {
        printf("mypoint[%d].x mypoint[%d].y = %d %d\n", i, i, mypoint[i].x, mypoint[i].y);
    }
    return 0;
}

実行結果
point[82].x point[82].y = 47 25
point[34].x point[34].y = 47 25
mypoint[0].x mypoint[0].y = 42 86
mypoint[1].x mypoint[1].y = 27 40
mypoint[2].x mypoint[2].y = 12 47



(4)構造体と関数

構造体を引数に持つこと,また関数の返す値にすることもできる。
点の座標をx軸に平行にdx,y軸に平行dyだけ移動した新しい点の座標を求める関数を作成してみる。
関数point_t movePoint(point_t point, int dx, int dy)は点pointをx軸に平行にdx,y軸に平行dyだけ移動した新しい点の座標を返す。
関数の型がpoint_t型になっている。

(例えば,座標(24,78)をx軸に平行に10,y軸に平行に-10だけ移動した新しい点を求めると(34,68)が求められる。)

point_t movePoint(point_t point, int dx, int dy)
{
    point_t newpoint;
    newpoint.x=point.x+dx;
    newpoint.y=point.y+dy;
    return newpoint;
}

この関数を利用したサンプルプログラムを次に示す。

List 8.2.4 point_t movePoint(point_t point, int dx, int dy)

#include <stdio.h>

/*point_t型の定義*/
typedef struct {
    int x; /*x座標*/
    int y; /*y座標*/
} point_t;

/*点pointをx軸に平行にdx,y軸に平行にdyだけ移動した新しい点の座標を返す関数*/
point_t movePoint(point_t point, int dx, int dy)
{
    point_t newpoint;
    newpoint.x=point.x+dx;
    newpoint.y=point.y+dy;
    return newpoint;
}

int main()
{
    point_t pnt0={24,78};
    point_t pnt1;
    pnt1=movePoint(pnt0,10,-10); /*pnt0をx方向に10,y方向に-10移動してpnt1を作る*/
    printf("pnt0=(%d,%d)\n",pnt0.x,pnt0.y);
    printf("pnt1=(%d,%d)\n",pnt1.x,pnt1.y);
    return 0;
}

/****実行結果****
pnt0=(24,78)
pnt1=(34,68)
****************/

この構造体を配列にして複数の点を表すようにした例を次に示す。

List 8.2.5 構造体を配列にした例

#include <stdio.h>

/*point_t型の定義*/
typedef struct {
    int x; /*x座標*/
    int y; /*y座標*/
} point_t;

/*点pointをx軸に平行にdx,y軸に平行にdyだけ移動した新しい点の座標を返す関数*/
point_t movePoint(point_t point, int dx, int dy)
{
    point_t newpoint;
    newpoint.x=point.x+dx;
    newpoint.y=point.y+dy;
    return newpoint;
}

int main()
{
    point_t pnt0[5]={{24,78},{10,10},{-10,10},{-10,-10},{10,-10}}; /*構造体の配列*/
    point_t pnt1[5]; /*構造体の配列*/
    int i;
    for (i=0;i<5;i++) {
        pnt1[i]=movePoint(pnt0[i],10,-10);
    }
    for (i=0;i<5;i++) {
        printf("pnt0[%d]=(%3d,%3d)  ",i,pnt0[i].x,pnt0[i].y);
        printf("pnt1[%d]=(%3d,%3d)\n",i,pnt1[i].x,pnt1[i].y);
    }
    return 0;
}

/****実行結果****
pnt0[0]=( 24, 78)  pnt1[0]=( 34, 68)
pnt0[1]=( 10, 10)  pnt1[1]=( 20,  0)
pnt0[2]=(-10, 10)  pnt1[2]=(  0,  0)
pnt0[3]=(-10,-10)  pnt1[3]=(  0,-20)
pnt0[4]=( 10,-10)  pnt1[4]=( 20,-20)
****************/


8.2.2 分数を構造体で表す
1つの分数を1つの変数で表す方法をC言語は提供することができる。

(1)構造体の導入
もし,分数を表す変数frtがあり,内部には2つの部屋があり,部屋の名前はnumeratorとdenominatorとし,変数frtの分子部屋に42,分母部屋に86を代入する時は(numerator:分子,denominator:分母)
  frt.numerator=42; frt.denominator=86;
 (英語がいやなら,部屋の名前をbunshiとbunboにしてもよい。この場合はfrt.bunshi=42; frt.bunbo=86となる)
とする。

このような変数を宣言するには次のように2段階がある。
第1段階 内部に2部屋あり部屋の名前がnumerator,denominatorである新しい変数型を作る (変数の型の名前をfraction_tとする)(fraction:分数
第2段階 fraction_t型の変数frtを宣言する
具体的には

/*fraction_t型の定義*/
typedef struct {
    int numerator; /*分子の部屋*/
    int denominator; /*分母の部屋*/
} fraction_t;

/*fraction_t型の変数の宣言*/
    fraction_t frt;

となる。このような宣言がなされているもとで
    frt.numerator=42; frt.denominator=86;
ができるようになる。

(2)構造体宣言時の初期値代入
変数宣言時の初期化代入をするなら
    fraction_t frt={42,86};
のように書くことができる。

    fraction_t f1={42,86}, f2={27,40}, f3={12,47};
のように記述し,分数 42/86, 27/40, 12/47 の3つの分数を表すことができる。

(3)構造体の配列
この型の変数の配列を作ったら
    fraction_t frt[100];
である。
    frt[82].numerator=47;frt[82].denominator=25;
というのは分数番号82の分子は47で,分母は25であるという意味である。この表記はあまり「ごりやく」が感じられない。
    frt[34]=frt[82];
というのは,分数番号82の分数を分数番号34にコピーしたことになる。(内部の部屋ごとにコピーが完了する)
変数宣言時の初期化代入をするなら
    fraction_t myfraction[3]={{42,86},{27,40},{12,47}};
というのは分数 42/86, 27/40, 12/47 の3つの分数を表している。
すこし,見通しがよくなったと感じられる。分数の数が100あっても,ある分数が,2箇所にばらばらに記述されることはない。

(4)
構造体と関数
構造体を引数に持つこと,また関数の返す値にすることもできる。
fraction_t reduceF
raction(fraction_t fraction)は引数で与えられた分数fractionを約分し,
約分結果を関数の返す値とする
関数である
関数の型がfraction_tになっている。
fraction
_t reduceF
raction(fraction_t fraction)
{
    int x=fraction.numerator;
   
int y=fraction.denominator;
    int r;
    /*ユークリッドの互除法で最大公約数を求める*/
    r=x%y:
    while (r!=0) {
        x=y;y=r;
        r=x%y;
    }
    /*ユークリッドの互除法おしまい この時点でyが最大公約数*/
    fraction.numerator/=y;
    fraction.denominator/=y;
    return fraction;
}
この関数を利用したサンプルプログラムを次に示す。

List 8.2.6 fraction_t reduceFraction(fraction_t fraction)

#include <stdio.h>

/*fraction_t型の定義*/
typedef struct {
    int numerator; /*分子*/
    int denominator; /*分母*/
} fraction_t;

/*分数を約分する関数,約分結果を関数の返す値として返す*/
fraction_t reduceFraction(fraction_t fraction)
{
    fraction_t myfrac;
    int x=fraction.numerator;
    int y=fraction.denominator;
    int r;
    /*ユークリッドの互除法で最大公約数を求める*/
    r=x%y;
    while (r!=0) {
        x=y;y=r;
        r=x%y;
    }
    /*ユークリッドの互除法おしまい この時点でyが最大公約数*/
    myfrac.numerator=fraction.numerator/y;
    myfrac.denominator=fraction.denominator/y;
    return myfrac;
}

int main()
{
    fraction_t frc0={24,96};
    fraction_t frc1;
    frc1=reduceFraction(frc0);
    printf("frc0= %d/%d\n",frc0.numerator,frc0.denominator);
    printf("frc1= %d/%d\n",frc1.numerator,frc1.denominator);
    return 0;
}

/****実行結果****
frc0= 24/96
frc1= 1/4
****************/

List 8.2.7 構造体の定義のみを変更したプログラム
(実行結果は変わらない)

#include <stdio.h>

/*bunsu_t型の定義*/
typedef struct {
    int bunshi; /*分子*/
    int bunbo; /*分母*/
} bunsu_t;

/*分数を約分する関数,約分結果を関数の返す値として返す*/
bunsu_t reduceFraction(bunsu_t fraction)
{
    fraction_t myfrac;
    int x=fraction.numerator;
    int y=fraction.denominator;
    int r;
    /*ユークリッドの互除法で最大公約数を求める*/
    r=x%y;
    while (r!=0) {
        x=y;y=r;
        r=x%y;
    }
    /*ユークリッドの互除法おしまい この時点でyが最大公約数*/
    myfrac.numerator=fraction.numerator/y;
    myfrac.denominator=fraction.denominator/y;
    return myfrac;
}

int main()
{
    bunsu_t frc0={24,96};
    bunsu_t frc1;
    frc1=reduceFraction(frc0);
    printf("frc0= %d/%d\n",frc0.bunshi,frc0.bunbo);
    printf("frc1= %d/%d\n",frc1.bunshi,frc1.bunbo);
    return 0;
}

/****実行結果****
frc0= 24/96
frc1= 1/4
****************/

新しく作った型の名前はどのようにつけてもかまわないが,新しい型の名前なのか変数の名前なのかがわかるように命名したほうがよい。型の名前(type name)ということが分かるように次のように工夫して作られることが多い。

規則

うしろに「_t」をつける point_t, fraction_t
うしろに「_type」をつける point_type, fraction_type
前に「T」をつけ,名前の第1文字を大文字にする TPoint, TFraction

コラム 構造体の宣言についての補足

構造体の宣言方法で,ここに取り上げた方法は,通常使われるが,背景はもうすこし複雑である。typedefを用いたので新しい型の名前をpoint_tとかfraction_tのように1単語で宣言できた。
しかしtypedefを用いない構造体の宣言方法もある。この方法では,「struct 構造体名」で1つの変数型の名前になる。例えば,次のように,構造体が宣言されると,型名は「struct point_type」が全プログラム中で「point_t」の代わりに使われる。

typedefを用いない構造体の宣言方法

#include <stdio.h>

/*stract point_type型の定義*/
struct point_type {
    int x; /*x座標*/
    int y; /*y座標*/
};

struct point_type movePoint(struct point_type point, int dx, int dy)
{
    struct point_type newpoint;
    newpoint.x=point.x+dx;
    newpoint.y=point.y+dy;
    return newpoint;
}

int main()
{
    struct point_type pnt0={24,78};
    struct point_type pnt1;
    pnt1=movePoint(pnt0,10,-10);
    printf("pnt0=(%d,%d)\n",pnt0.x,pnt0.y);
    printf("pnt1=(%d,%d)\n",pnt1.x,pnt1.y);
    return 0;
}

typedefを用いた構造体の宣言方法(タグ名を残したもの)
「point_type」がタグ名

#include <stdio.h>

/*stract point_type型の定義*/
typedef struct point_type { /*←このpoint_typeがタグ名*/
    int x; /*x座標*/
    int y; /*y座標*/
}
point_t;

point_t movePoint(point_t point, int dx, int dy)
{
    point_t newpoint;
    newpoint.x=point.x+dx;
    newpoint.y=point.y+dy;
    return newpoint;
}

int main()
{
    point_t pnt0={24,78};
    point_t pnt1;
    pnt1=movePoint(pnt0,10,-10);
    printf("pnt0=(%d,%d)\n",pnt0.x,pnt0.y);
    printf("pnt1=(%d,%d)\n",pnt1.x,pnt1.y);
    return 0;
}

typedefを用いた構造体の宣言方法(タグ名を省略したもの)
タグ名「point_type」は使われないから省略

#include <stdio.h>

/*stract point_type型の定義*/
typedef struct { /*ここにタグ名が省略されている*/
    int x; /*x座標*/
    int y; /*y座標*/
}
point_t;

point_t movePoint(point_t point, int dx, int dy)
{
    point_t newpoint;
    newpoint.x=point.x+dx;
    newpoint.y=point.y+dy;
    return newpoint;
}

int main()
{
    point_t pnt0={24,78};
    point_t pnt1;
    pnt1=movePoint(pnt0,10,-10);
    printf("pnt0=(%d,%d)\n",pnt0.x,pnt0.y);
    printf("pnt1=(%d,%d)\n",pnt1.x,pnt1.y);
    return 0;
}


typedefとは「ある変数型に別の変数型名をつけなおす」の意味である。
例えば「長さ」をしまうために,length_t型を作りたい場合は
typedef double length_t;
と書くと,その後は
    length_t top, bottom;
のように書くことができるようになる。これは見た目が異なるだけで
    double top, bottom;
のように書いたのと意味はまったく変わらないが,文脈上スマートである。

構造体定義にtypedefを使った場合

typedef struct point_type {
    int x; /*x座標*/
    int y; /*y座標*/
} point_t;

struct point_type {
    int x; /*x座標*/
    int y; /*y座標*/
}


point_t型として今後使いますという意味になる。

本文で用いているtypedefでは構造体タグ名(ここでは「point_type」)が省略された形である。
この 構造体タグ名を省略できない場合もある。将来用いるリスト構造の要素構造体などでは構造体のポインタを構造体要素に持つ。C言語では,使用する名前は事前に定義されていなければならないので,構造体タグ名は必須となる。

typedef struct point_type {
    int x; /*x座標*/
    int y; /*y座標*/
    point_type *ptrpoint; /*自分とおなじ構造体を指すポインタ*/
} point_t;

使用する名前は事前に定義されていなければならないので次のようには書けない

typedef struct { /*コンパイルエラーになる例*/
    int x; /*x座標*/
    int y; /*y座標*/
    point_t *ptrpoint; /*自分とおなじ構造体を指すポインタを宣言したかったが*/
} point_t;


 課題 8.1

(1)関数point_t rotatePoint(point_t point, double theta)は点pointを原点を中心に反時計回りにthetaラジアン回転した新しい点の座標を返す。この関数を作成し,テスト用のmain()も作りなさい。(p08ex01.c)

ただし
typedef struct {
    int x; /*x座標*/
    int y; /*y座標*/
} point_t;
である。
(x,y)を反時計回りにθラジアン回転すると( x cosθ - y sinθ , x sinθ + y cosθ )になる。
プログラムで点(100,100)を反時計回りにπ/4と-π/4回転すると(0,141),(141,0)になることを確認しなさい。
ただし,関数sin,cosの引数はradの単位であることに注意しなさい。
表示にあたっては「"(%d,%d)"」の書式で表示しなさい。

(2)分数の構造体を用いた四則演算関数を作成しなさい。 (p08ex02.c)

ただし,分数を表す構造体は
/*fraction_t型の定義*/
typedef struct {
    int numerator; /*分子*/
    int denominator; /*分母*/
} fraction_t;
で与えられるものとする。
また負の分数のは分子のみが負になるものとし,分母は必ず正の整数であることにする。
まず初めは,List 8.2.6の約分関数を正負対応に拡張しなさい。(List 8.2.6 fraction_t reduceFraction(fraction_t fraction)は負の分数を想定していない。)

次に四則演算の関数は次の4つとする。

これらの関数は内部でreduceFractionを呼び出してもよい。
1)2つの分数a,bを受け取り和(a+b)を約分して返す関数
fraction_t addFraction(fraction_t a,fraction_t b)
2)2つの分数a,bを受け取り差(a-b)を約分して返す関数
fraction_t subtractFraction(fraction_t a,fraction_t b)
3)2つの分数a,bを受け取り積(a*b)を約分して返す関数
fraction_t multiplyFraction(fraction_t a,fraction_t b)
4)2つの分数a,bを受け取り商(a/b)を約分して返す関数
fraction_t divideFraction(fraction_t a,fraction_t b)
 ただし,bの分子が0の時は10000000/1を返すものとする。

検査用main()を作り,次の値で検査し,ヒントと同じ書式で表示しなさい。
1/6,1/3で四則演算(差,商については順番も換えること)
1/6,-1/3で四則演算(差,商については順番も換えること)
-1/6,1/3で四則演算(差,商については順番も換えること)
-1/6,-1/3で四則演算(差,商については順番も換えること)
3/4,16/3で四則演算(差,商については順番も換えること)
3/4,-16/3で四則演算(差,商については順番も換えること)
-3/4,16/3で四則演算(差,商については順番も換えること)
-3/4,-16/3で四則演算(差,商については順番も換えること)

使用すべきテスト用関数

 8.3 データベースの構造体

データベースでも構造体が用いられている。
例えば映画のデータベースを作ろう。1つの映画が持っている「属性」にはさまざまなものがあるが,ここでは「題名」「主演」「監督」「公開年度」持たせることにする。

typedef struct {
    char title[64];       /*題名*/
    char leadingAct[64] ; /*主演俳優*/
    char director[64];    /*監督*/
    int year;             /*公開*/
} movie_t;

List 8.3.1 movie_tを使ったデータベースの基本例

#include <stdio.h>
#include <string.h>

/*movie_t型の定義*/
typedef struct {
    char title[64];       /*題名*/
    char leadingAct[64] ; /*主演俳優*/
    char director[64];    /*監督*/
    int year;             /*公開*/
} movie_t;

movie_t myCollection[]={
    {"Planet of the Apes","Charlton Heston","Franklin J. Schaffner",1968},
    {"Lord of the Rings, The","Elijah Wood","Peter Jackson",2001},
    {"Saving Private Ryan","Tom Hanks","Steven Spielberg",1998},
    {"Gone with the Wind","Vivien Leigh","Victor Fleming",1939},
    {"Schindler's List","Liam Neeson","Steven Spielberg",1993},
    {"My Neighbor TOTORO","Satsuki","Hayao Miyazaki",1988},
    {"Braveheart","Mel Gibson","Mel Gibson",1995},
    {"Dances with Wolves","Kevin Costner","Kevin Costner",1990},
    {"Born on the Fourth of July","Tom Cruise","Oliver Stone",1989},
    {"Platoon","Tom Berenger","Oliver Stone",1986},
    {"Kramer vs. Kramer","Dustin Hoffman","Robert Benton",1979},
    {"Sting, The","Paul Newman","George Roy Hill",1973},
    {"Sound of Music, The","Julie Andrews","Robert Wise (I)",1965},
    {"My Fair Lady","Audrey Hepburn","George Cukor",1964},
    {"Titanic","Leonardo DiCaprio","James Cameron",1997},
    {"Princess Mononoke","Ashitaka","Hayao Miyazaki",1997}
};

/*年代順ソート*/
void sortMoviesInYear(movie_t movie[],int number)
{
    int i,j;
    movie_t tmp;
    for (i=0;i<number-1;i++) {
        for (j=i+1;j<number;j++) {
            if (movie[i].year<movie[j].year) {
                tmp=movie[i];
                movie[i]=movie[j];
                movie[j]=tmp;
            }
        }
    }
}

/*タイトル順ソート*/
void sortMoviesInTitle(movie_t movie[],int number)
{
    int i,j;
    movie_t tmp;
    for (i=0;i<number-1;i++) {
        for (j=i+1;j<number;j++) {
            if (0<strcmp(movie[i].title,movie[j].title)) {
                tmp=movie[i];
                movie[i]=movie[j];
                movie[j]=tmp;
            }
        }
    }
}

/*全映画表示*/
void printMovies(movie_t movie[],int number)
{
    int i;
    for (i=0;i<number;i++) {
        printf("%-28s %-20s %-22s %4d\n",
            movie[i].title,
            movie[i].leadingAct,
            movie[i].director,
            movie[i].year);
    }
}

int main()
{
    int numberOfMovies=sizeof(myCollection)/sizeof(movie_t);

    printf("** Original data **\n");
    printMovies(myCollection,numberOfMovies);

    sortMoviesInTitle(myCollection,numberOfMovies);
    printf("** Title order **\n");
    printMovies(myCollection,numberOfMovies);

    sortMoviesInYear(myCollection,numberOfMovies);
    printf("** Year order **\n");
    printMovies(myCollection,numberOfMovies);
    return 0;
}

/************************** 実行結果 **************************************
** Original data **
Planet of the Apes           Charlton Heston      Franklin J. Schaffner  1968
Lord of the Rings, The       Elijah Wood          Peter Jackson          2001
Saving Private Ryan          Tom Hanks            Steven Spielberg       1998
Gone with the Wind           Vivien Leigh         Victor Fleming         1939
Schindler's List             Liam Neeson          Steven Spielberg       1993
My Neighbor TOTORO           Satsuki              Hayao Miyazaki         1988
Braveheart                   Mel Gibson           Mel Gibson             1995
Dances with Wolves           Kevin Costner        Kevin Costner          1990
Born on the Fourth of July   Tom Cruise           Oliver Stone           1989
Platoon                      Tom Berenger         Oliver Stone           1986
Kramer vs. Kramer            Dustin Hoffman       Robert Benton          1979
Sting, The                   Paul Newman          George Roy Hill        1973
Sound of Music, The          Julie Andrews        Robert Wise (I)        1965
My Fair Lady                 Audrey Hepburn       George Cukor           1964
Titanic                      Leonardo DiCaprio    James Cameron          1997
Princess Mononoke            Ashitaka             Hayao Miyazaki         1997
** Title order **
Born on the Fourth of July   Tom Cruise           Oliver Stone           1989
Braveheart                   Mel Gibson           Mel Gibson             1995
Dances with Wolves           Kevin Costner        Kevin Costner          1990
Gone with the Wind           Vivien Leigh         Victor Fleming         1939
Kramer vs. Kramer            Dustin Hoffman       Robert Benton          1979
Lord of the Rings, The       Elijah Wood          Peter Jackson          2001
My Fair Lady                 Audrey Hepburn       George Cukor           1964
My Neighbor TOTORO           Satsuki              Hayao Miyazaki         1988
Planet of the Apes           Charlton Heston      Franklin J. Schaffner  1968
Platoon                      Tom Berenger         Oliver Stone           1986
Princess Mononoke            Ashitaka             Hayao Miyazaki         1997
Saving Private Ryan          Tom Hanks            Steven Spielberg       1998
Schindler's List             Liam Neeson          Steven Spielberg       1993
Sound of Music, The          Julie Andrews        Robert Wise (I)        1965
Sting, The                   Paul Newman          George Roy Hill        1973
Titanic                      Leonardo DiCaprio    James Cameron          1997
** Year order **
Lord of the Rings, The       Elijah Wood          Peter Jackson          2001
Saving Private Ryan          Tom Hanks            Steven Spielberg       1998
Princess Mononoke            Ashitaka             Hayao Miyazaki         1997
Titanic                      Leonardo DiCaprio    James Cameron          1997
Braveheart                   Mel Gibson           Mel Gibson             1995
Schindler's List             Liam Neeson          Steven Spielberg       1993
Dances with Wolves           Kevin Costner        Kevin Costner          1990
Born on the Fourth of July   Tom Cruise           Oliver Stone           1989
My Neighbor TOTORO           Satsuki              Hayao Miyazaki         1988
Platoon                      Tom Berenger         Oliver Stone           1986
Kramer vs. Kramer            Dustin Hoffman       Robert Benton          1979
Sting, The                   Paul Newman          George Roy Hill        1973
Planet of the Apes           Charlton Heston      Franklin J. Schaffner  1968
Sound of Music, The          Julie Andrews        Robert Wise (I)        1965
My Fair Lady                 Audrey Hepburn       George Cukor           1964
Gone with the Wind           Vivien Leigh         Victor Fleming         1939
****************************************************************************/

 8.4 複素数の構造体と四則演算

構造体は複数の値や文字列である概念を表すものなので,関数群が作られることが多い。
この考え方は,オブジェクト指向プログラミングの入口となる。

複素数の構造体を定義し,四則演算の関数を作成した例を示す。

List 8.4.1 複素数の構造体定義,四則演算の関数,検査関数

#include <stdio.h>

/*complex_t型の定義*/
typedef struct {
    double real; /*real part 実部*/
    double imag; /*imaginary part 虚部*/
} complex_t;

/*複素数の和*/
complex_t addComplex(complex_t a, complex_t b)
{
    complex_t ret;
    ret.real=a.real+b.real;
    ret.imag=a.imag+b.imag;
    return ret;
}

/*複素数の差*/
complex_t subtractComplex(complex_t a, complex_t b)
{
    complex_t ret;
    ret.real=a.real-b.real;
    ret.imag=a.imag-b.imag;
    return ret;
}

/*複素数の積*/
complex_t multiplyComplex(complex_t a, complex_t b)
{
    complex_t ret;
    ret.real=a.real*b.real-a.imag*b.imag;
    ret.imag=a.real*b.imag+a.imag*b.real;
    return ret;
}

/*複素数の商*/
complex_t divideComplex(complex_t a, complex_t b)
{
    complex_t ret;
    double denominator;
    if (b.real==0.&&b.imag==0.) {
        ret.real=1e10;
        ret.imag=1e10;
    } else {
        denominator=b.real*b.real+b.imag*b.imag;
        ret.real=(a.real*b.real+a.imag*b.imag)/denominator;
        ret.imag=(a.real*b.imag+a.imag*b.real)/denominator;
    }
    return ret;
}

/*四則演算の検査関数*/
void operations(complex_t a,complex_t b)
{
    complex_t x;
    printf("(%f)+i(%f) (%f)+i(%f)\n",a.real,a.imag,b.real,b.imag);
    x=addComplex(a,b);
    printf("[+] (%f)+i(%f)\n",x.real,x.imag);
    x=subtractComplex(a,b);
    printf("[-] (%f)+i(%f)\n",x.real,x.imag);
    x=multiplyComplex(a,b);
    printf("[*] (%f)+i(%f)\n",x.real,x.imag);
    x=divideComplex(a,b);
    printf("[/] (%f)+i(%f)\n",x.real,x.imag);
}

int main()
{
    complex_t test1[2]={{-2,3},{3,4}};
    complex_t test2[2]={{-2,-3},{3,4}};
    complex_t test3[2]={{2,3},{-3,4}};
    complex_t test4[2]={{-2,3},{3,-4}};
    operations(test1[0],test1[1]);
    operations(test2[0],test2[1]);
    operations(test3[0],test3[1]);
    operations(test4[0],test4[1]);
    return 0;
}

/****             実行結果            ****
(-2.000000)+i(3.000000) (3.000000)+i(4.000000)
[+] (1.000000)+i(7.000000)
[-] (-5.000000)+i(-1.000000)
[*] (-18.000000)+i(1.000000)
[/] (0.240000)+i(0.040000)
(-2.000000)+i(-3.000000) (3.000000)+i(4.000000)
[+] (1.000000)+i(1.000000)
[-] (-5.000000)+i(-7.000000)
[*] (6.000000)+i(-17.000000)
[/] (-0.720000)+i(-0.680000)
(2.000000)+i(3.000000) (-3.000000)+i(4.000000)
[+] (-1.000000)+i(7.000000)
[-] (5.000000)+i(-1.000000)
[*] (-18.000000)+i(-1.000000)
[/] (0.240000)+i(-0.040000)
(-2.000000)+i(3.000000) (3.000000)+i(-4.000000)
[+] (1.000000)+i(-1.000000)
[-] (-5.000000)+i(7.000000)
[*] (6.000000)+i(17.000000)
[/] (-0.720000)+i(0.680000)
********************************************/

 

 課題 8.2
(3)8.3の映画のデータベースを参考にデータベースを作り,適当にソートして表示できるようにしなさい。 (p08ex03.c)

データベースの対象は次のようなものにしなさい。
好きな音楽CD,好きなミュージシャン,好きな野球選手,好きなサッカー選手
また,その属性の例としては
ミュージシャン 名前 デビュー年 代表曲
のようにしなさい


(文章課題)構造体に関するこのページの内容で重要なポイントをまとめてレポートにしなさい。ただし,初めの2行(ファイル名,ID,出席番号,氏名)は,これまでのプログラムと同様な書式で書くこと。 (p08.txt)

ただし,次の内容は必ず含むこと
どうして分数や時間に関するデータは構造体を使うとよいのか。
データベースを作るとき,どうして構造体が使われるのか。
構造体の定義の仕方を例を挙げて説明しなさい。