8.構造体
Copyright(C) 14Mar2003
coskx
構造体を用いると複数の数値や文字列で1つの概念を形成するものの記述が楽になる。
ここで修得してほしい内容は以下の通りである。
1.複数の値で1つの概念を形成するものは構造体を導入すると表現が自然になることを理解する。
2.新しい型の定義により,構造体を利用可能にする。
3.構造体の変数の任意のメンバに対する値の設定や参照が自由に出来る
4.構造体がデータベースに用いられる例を理解する
5.複素数の構造体を例に取り,有用な関数群を整備する手法を理解する。
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点もあると,見通しが悪い。
分数をプログラムで表現することを考えよう。分数は
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個もあると,見通しが悪い。
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) ****************/
|
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 reduceFraction(fraction_t fraction)は引数で与えられた分数fractionを約分し,
約分結果を関数の返す値とする関数である。
関数の型がfraction_tになっている。
fraction_t reduceFraction(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; |
(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で四則演算(差,商については順番も換えること)
使用すべきテスト用関数
データベースでも構造体が用いられている。
例えば映画のデータベースを作ろう。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 ****************************************************************************/ |
構造体は複数の値や文字列である概念を表すものなので,関数群が作られることが多い。
この考え方は,オブジェクト指向プログラミングの入口となる。
複素数の構造体を定義し,四則演算の関数を作成した例を示す。
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) ********************************************/ |
(3)8.3の映画のデータベースを参考にデータベースを作り,適当にソートして表示できるようにしなさい。 (p08ex03.c)
データベースの対象は次のようなものにしなさい。
好きな音楽CD,好きなミュージシャン,好きな野球選手,好きなサッカー選手
また,その属性の例としては
ミュージシャン 名前 デビュー年 代表曲
のようにしなさい
(文章課題)構造体に関するこのページの内容で重要なポイントをまとめてレポートにしなさい。ただし,初めの2行(ファイル名,ID,出席番号,氏名)は,これまでのプログラムと同様な書式で書くこと。 (p08.txt)
ただし,次の内容は必ず含むこと
どうして分数や時間に関するデータは構造体を使うとよいのか。
データベースを作るとき,どうして構造体が使われるのか。
構造体の定義の仕方を例を挙げて説明しなさい。