Previous: 構造体の初期化 II
Up: 構造体 III
Previous Page: 構造体の初期化 II
Next Page: About this document ...
関数の返り値は一つであり、関数の返り値を用いて値をやり取りする限りでは、通常は いくつもの値を受け取る事は出来ないが、構造体を使えば返り値に幾つもの値を持た せる事が出来る。しかし、その時には、何を返り値にするべきなのか、従って、関数 はどういう設計をすればよいのか、という設計指針の問題が生じる。構造体によって 幾つもの値を関数の返り値に出来るからと言って、行き当たりばったりに関数を作成 してはならない。あくまでも関数は機能本位に、どういう値に対して何をするものか を良く考えて作成しなければならない。
ここでは、ロールプレイングゲームを例に取って考えよう。 ロールプレイングゲームでは、まず player がいる。 player は、 色々なところを歩き回るので、位置をデータに持っている必要がある。 また、 player が成長していくのならば、そのレベルを記録しておかねば ならない。ところが、レベルと言っても、攻撃力や守備力、魔法が使えるならば 魔法力、体力など様々な値が必要である。更に、 player は所持品を持ってい る場合もあるだろう。
例えば、 player の位置は、(x,y) 座標で、レベルは体力、攻撃力、守備力 があり、所持品は剣と鎧を持てるとすると、 player を表す構造体は、次の ように書ける。
例6 struct Point { int x,y;}; /* 位置 */ struct Lebel { /* レベル */ int life; /* 体力 */ int offence; /* 攻撃力 */ int defence; /* 守備力 */ }; struct Property { /* 所持品 */ int sword; /* 剣 */ int armor; /* 鎧 */ };struct Player { struct Point point; struct Lebel lebel; struct Property property; };
一方、 player がマップの中を歩いたりするような場合は、そのための関数を 作成し、その関数の中で、位置を変更するようにすることになる。この場合、 例えば、関数の型、引数は次のようになる。
例7 struct Point aruku( struct Point p){ ... return p; }
これを用いる時は、次のように、 struct Player 型のメンバーの構造体だけ を使って受け渡しをする。
例8 struct Player my; ... my.point = aruku( my.point );
重要な点は、 my.point は、 struct Point 型の構造体である点で、 余分なデータは渡さないことである。
一方、プレイヤーの生死の判断や、敵との遭遇の判断等は結果が明らかに真偽で あらわせられる場合は、関数の返り値は構造体にせずに真又は偽にしておいた方が 良いだろう。問題は、敵との戦闘などプレイヤーや敵のデータに変更があり、なおかつ 関数の返り値も欲しい ( 戦闘が終わったかどうか等 ) の場合である。 こうした場合、解決方法は2つある。
しかし、多くの場合において見通しの良い方法は、関数設計を見直す事である。 初心のうちは、つい簡単な2番目の方法を使ってしまいがちであるが、 プログラムを改造している最中に安易に2番目の方法のみを行うと、段々と プログラムが複雑になり、バグの原因にもなりかねない。 こうした事態は初めから避け、「君子危うきに近寄らず」がプログラミングでも 有効な戦略なのである。
課題19
cd c を実行した後で( /c に移動した後で )、以下の課題を やってみよ。
課題 18.2 の三角形の頂点座標の初期化を本文の方法で行え。
課題 13.2 のプログラムを以下のように変更せよ。
変更前 main() { ... for(;;){ if( no_fight()){ break; } if( fight() ){ break; } } ... }変更後 main() { ... fight(); ... }
関数 fight は全面的に書き換える。 fight(){ for(;;){ /* change */ if( no_fight()){ break; } /* change */ printf("敵と戦います。\n");
enemy_life -= my_offence/enemy_defence;
if (enemy_life <= 0){ printf("敵を倒しました。\n"); enemy--; num_enemy++; break; /* change */ }else{ my_life -= enemy_offence/my_defence; }
if (my_life <= 0){ printf("あなたは死にました。\n"); break; /* change */ }else{ printf("敵の生命力は %d, あなたの生命力は %d\n", enemy_life, my_life); } } return; /* change */ }
以上の変更をした上で、関数の定義、宣言を書き換え、実行してみよ。
参考 以上の変更は、次の課題のための関数の設計変更になっている。
課題 19.3 において、外部変数の使用を止め、構造体を用いて 書き換えよ。但し、構造体の宣言は関数の外で行い、構造体変数の確保は、 関数内部で行う事。
作成したプログラムをメイルで金山まで送りなさい。題は、kadai19 とする事。