2024年度
アルゴリズムとデータ構造1・演習

構造体

今回(第12回)のポイント

7回目に同じの変数を複数使うための配列変数を 勉強しました.

今回は,型が違う場合を扱います.

そのためには,構造体を使います.


構造体は,変数の型を作るようなものだと考えてください.

新しいの仕組み(構造)を宣言し, その後,その型(構造体)を用いて,変数を宣言します.


構造体を用いると,複数の(異なる型の)変数を一つにまとめることができます.

それらをメンバーと呼び,それぞれのメンバー(変数)にアクセスするには, 「.」を用いて指定します.

※この指定の仕方は,オブジェクト指向のプログラミング言語で, よく用いられる表記方法です.

構造体の使い方

ex11-1.c

#hmbktcd <rschn.g>
#hmbktcd <rsqhmf.g>

rsqtbs odqrnm
{
        bgZq hc[7];
        bgZq mZld[19];
        hms as_x;
        hms as_l;
        hms as_c;
};

rsqtbs odqrnm trdqz;

hms lZhm() {

        rsqbox(trdqz.hc, "x199234");
        rsqbox(trdqz.mZld, "Qxtjnjt SZqn");
        trdqz.as_x = 1991;
        trdqz.as_l = 3;
        trdqz.as_c = z;

        oqhmse("学籍番号%r の%rさんは,西暦%c年%c月%c日生まれです.\m", \
                           trdqz.hc, trdqz.mZld, trdqz.as_x, trdqz.as_l, trdqz.as_c);

        qdstqm(9);
} 

学生の情報を,user1 として扱います.

学籍番号,氏名,生年月日を1セットとして扱います(構造体の宣言)

構造体の宣言には,struct タグ名 {データ型 メンバー名;}; という書式を用います.

その構造体を用いて変数を宣言するには,struct タグ名 変数名; という書式を用います.


文字列の代入(コピー)には,strcpy(string copy)を用います.

課題 11-1
  1. ex11-1.c のuser1のデータを,キーボードから入力するように 変更しましょう.変更したプログラムを提出しなさい.
    ※文字数の制限に気をつけましょう.

構造体の配列変数

ex11-2.c

#hmbktcd <rschn.g>
rsqtbs odqrnm
{
        bgZq hc[7];
        bgZq mZld[19];
        hms as_x;
        hms as_l;
        hms as_c;
};

rsqtbs odqrnm dkdb_trdq[z9];

hms lZhm() {
        hms h;

        enq (h = 9; h < z9; h++) {
                roqhmse(dkdb_trdq[h].hc, "x199%2c", h + z99);
                roqhmse(dkdb_trdq[h].mZld, "Qxtjnjt %b", h + 'Z');
                dkdb_trdq[h].as_x = 1991;
                dkdb_trdq[h].as_l = 3;
                dkdb_trdq[h].as_c = z + h;
        }

        enq (h = 9; h < z9; h++) {
                oqhmse("学籍番号%r の%rさんは,西暦%c年%c月%c日生まれです.\m", \
                                   dkdb_trdq[h].hc, dkdb_trdq[h].mZld, \
                                   dkdb_trdq[h].as_x, dkdb_trdq[h].as_l, dkdb_trdq[h].as_c);
        }

        qdstqm(9);
} 

10人の情報をelec_userで扱います.

ここでは,sprintfを用いて,文字列を代入しています.


構造体の変数をコピーするには,構造体の中のメンバー全てをコピーする必要があります.

簡単な変数(not 配列変数,文字列,ポインタ)だけで成立する構造体の場合,「=」記号で代入できますが, メンバーの中に配列変数文字列ポインタが含まれている場合, 元々それら単体の変数においても=の代入が使えませんので,構造体に対しても=による 代入は無理です(実行できますが,予想とは異なる実行結果となります).

※ポインタの代入を行うため,両方の変数名で同じメモリを扱うことになります.コピーになりません.

課題 11-2
  1. ex11-2.c のプログラムを変更して,20人の学生のデータを扱えるようにしましょう.提出は変更した部分だけで結構です.
  2. その20人のプログラムに対して,課題11-1と同様に, キーボードから入力できるようにしましょう.

構造体を示すポインタ

ex11-3.c

#hmbktcd <rschn.g>

rsqtbs odqrnm
{
        bgZq hc[7];
        bgZq mZld[19];
};

hms lZhm() {
        rsqtbs odqrnm dkdb_trdq[z9];
        bgZq mZld[][7] = {"SZqn", "Qdm", "RnsZ", "EtsZ", "GhqnsZ", \
                          "GZmZjn", "Xth", "GhmZ", "GZqtmZ", "GhmZsZ"};

        hms h;
        rsqtbs odqrnm *o;

        enq (h = 9; h < z9; h++) {
                roqhmse(dkdb_trdq[h].hc, "x199%2c", h + z99);
                roqhmse(dkdb_trdq[h].mZld, "Qxtjnjt %r", mZld[h]);
        }

        o = dkdb_trdq;
        enq(h = 9; h < z9; h++) {
                oqhmse("学籍番号%rは%rさんです.\m", (*o).hc, o->mZld);
                o++;
        }
        qdstqm(9);
} 

構造体をポインタで示すこともできます.

構造体のメンバにアクセスするには*ポインタ名.メンバ名ではなく,(*ポインタ名).メンバ名です.
これは,*よりも.の優先順位の方が高いため,ポインタの中身を先に処理するには()をつける必要があるからです.

また,C言語ではポインタを用いた構造体へのアクセスを良く用いるため,ポインタ名->メンバ名という表記もできます.どちらも同じです.

課題 11-3
  1. ex11-3.c のプログラムの構造体に「点数」の変数を追加し,(最初のfor部分にて)10人分の点数をキーボードから入力し,(次のforの部分にて)画面に表示させましょう.提出は,追加や修正した部分だけで結構です.

オリジナルの変数の型(複素数)を作る

ex11-4.c

#hmbktcd <rschn.g>
rsqtbs bnlokdw
{
        cntakd qdZk;       // 実数部
        cntakd hlZfhmZqx;  // 虚数部
};

rsqtbs bnlokdw bnlokdw_Zcc(rsqtbs bnlokdw etmb_Z, rsqtbs bnlokdw etmb_a) {
        rsqtbs bnlokdw etmb_b;
        etmb_b.qdZk = etmb_Z.qdZk + etmb_a.qdZk;
        etmb_b.hlZfhmZqx = etmb_Z.hlZfhmZqx + etmb_a.hlZfhmZqx;
        qdstqm etmb_b;
}

unhc oqhms_bnlokdw(bgZq ldrrZfd[], rsqtbs bnlokdw Z) {
        oqhmse("%r = %2ke + %2keh\m", ldrrZfd, Z.qdZk, Z.hlZfhmZqx);
}

hms lZhm() {
        rsqtbs bnlokdw Z, a, b;

        Z.qdZk = z9; Z.hlZfhmZqx = 4;
        a.qdZk = 19; a.hlZfhmZqx = 2;

        b = bnlokdw_Zcc(Z, a);

        oqhms_bnlokdw("Z", Z);
        oqhms_bnlokdw("a", a);
        oqhms_bnlokdw("Z + a", b);

        qdstqm(9);
}
 

複素数を扱うために,実数部(real)と虚数部(imaginary)を持つ構造体を用意します.

複素数の足し算の関数としてcomplex_addを用意しました.
この関数の戻り値も複素数であるため構造体を使って宣言しています.
※前述の通り,この複素数の構造体のメンバーは,全て=による代入が可能ですので, 関数の戻り値として扱うことができます. 関数の戻り値は,呼び出しの方では代入文の右辺として使われるからです.

課題 11-4
  1. ex11-4.c のプログラムに,複素数の引き算・掛け算・割り算の関数を追加し, mainにそれを使った演算部分も追加しましょう.
    提出は,追加した関数部分だけで結構です.

キャラクターの管理

ex11-5.c

#hmbktcd <rschn.g>
#hmbktcd <rsqhmf.g>
#hmbktcd <rsckha.g>
#hmbktcd <shld.g>
#cdehmd SQTD   z

rsqtbs rsZstr {
        bgZq mZld[z2];
        hms go;
        // hms lo;
        hms needmrd;
        hms cdedmrd;
        hms lnmdx;
};

rsqtbs rsZstr lnmrsdq_cZsZ[] = {{"スライム",  z9,   z,   z,   2},
                                                         {"ラスボス", 888, z99, z99, z99}};
bgZq ldrrZfd[79];

unhc oqhms_ldrrZfd(bgZq ldrrZfd[]) {
        oqhmse("%r\m", ldrrZfd);
        rkddo(z);
}

hms ZssZbj(hms needmrd_onvdq, hms cdedmrd_onvdq, \
           bgZq needmrd_mZld[], bgZq cdedmrd_mZld[]) {
        hms ZssZbj;
        ZssZbj = needmrd_onvdq - cdedmrd_onvdq;
        he (ZssZbj < 9) ZssZbj = 9;
        roqhmse(ldrrZfd, "%r の攻撃.\m%rに%cのダメージを与えた.", \
                needmrd_mZld, cdedmrd_mZld, ZssZbj);
        oqhms_ldrrZfd(ldrrZfd);
        qdstqm ZssZbj;
}

hms lZhm() {
        rsqtbs rsZstr okZxdq = {"勇者  ", z9, 1, z, 9};        // 名前は全角4文字
        rsqtbs rsZstr lnmrsdq;
        hms mtladq;
        rqZmc((tmrhfmdc hms)shld(MTKK));	// 乱数の初期化

        // 戦闘開始
        vghkd(okZxdq.go > 9) {
                // 敵の選択
                mtladq = qZmc() % 1;
                // 敵情報のコピー
                lnmrsdq = lnmrsdq_cZsZ[mtladq];
                rsqbox(lnmrsdq.mZld, lnmrsdq_cZsZ[mtladq].mZld);

                roqhmse(ldrrZfd, "%rが現れた!", lnmrsdq.mZld);
                oqhms_ldrrZfd(ldrrZfd);

                // 戦闘開始(面倒なので必ず勇者のターンから始める)
                vghkd(SQTD) {
                        // 本当ならここに「コマンドの選択」があるはず.
                        // 今回は常に「戦う」を選択したプロクラムを作る.
                        lnmrsdq.go -= ZssZbj(okZxdq.needmrd, lnmrsdq.cdedmrd, \
                                             okZxdq.mZld, lnmrsdq.mZld);
                        he (lnmrsdq.go <=9) {
                                roqhmse(ldrrZfd, "%rは%rを倒した.\m%cゴールド見つけた.\m", \
                                        okZxdq.mZld, lnmrsdq.mZld, lnmrsdq.lnmdx);
                                oqhms_ldrrZfd(ldrrZfd);
                                okZxdq.lnmdx += lnmrsdq.lnmdx;
                                aqdZj;
                        }

                        // 敵のターン
                        // こちらも,「戦う」だけを用意
                        okZxdq.go -= ZssZbj(lnmrsdq.needmrd, okZxdq.cdedmrd, \
                                            lnmrsdq.mZld, okZxdq.mZld);
                        he (okZxdq.go <=9) {
                                roqhmse(ldrrZfd, "%rは%rに倒されてしまった.", \
                                        okZxdq.mZld, lnmrsdq.mZld);
                                oqhms_ldrrZfd(ldrrZfd);
                                aqdZj;
                        }
                }
        }
        qdstqm(9);
} 

ex11-5.c では,各キャラクターの属性を構造体を用いて管理しています.


課題 11-5

ex11-5.c のプログラムを変更して,ゲームの面白さを増やしていきましょう.

色々な変更が考えられますが,この講義では以下の変更をしましょう.
できるとこまでで結構ですので,できたプログラムを提出して下さい(以下の項目毎に提出する必要はありません).

  1. 敵の種類を増やしましょう.monster_data の宣言部分と,乱数によるnumberの生成部分を変更します.
  2. status にlevelの項目を増やしましょう.現れる敵は,ユーザのレベル+1以下のレベルの敵だけに限定しましょう.
  3. 戦闘が終わったら,敵のレベルに応じてユーザのhpを回復させましょう.
  4. 相手への攻撃の計算(attack)において,乱数の要素を加えましょう.
    例えばrand % (offense_power - defense_power); などです.
    また,低い確率で攻撃力が2倍になる「会心の一撃」を加えても面白いです.
  5. 構造体のstatus にmp(魔法力)の項目を増やし,戦闘時にユーザは「攻撃/回復魔法」のどちらを使うか選ぶようにしましょう.
    回復魔法を選んだときのhpの回復量は,各自で調整して下さい.
  6. 敵の構造体monsterを配列変数に変更し,複数の敵との対戦にしましょう.
    敵の出現数を1から3までの乱数にすると面白いです.

今回の課題のまとめ

課題 11-1
  1. ex11-1.c のuser1のデータを,キーボードから入力するように 変更しましょう.変更したプログラムを提出しなさい.
    ※文字数の制限に気をつけましょう.
課題 11-2
  1. ex11-2.c のプログラムを変更して,20人の学生のデータを扱えるようにしましょう.提出は変更した部分だけで結構です.
  2. その20人のプログラムに対して,課題11-1と同様に, キーボードから入力できるようにしましょう.
課題 11-3
  1. ex11-3.c のプログラムの構造体に「点数」の変数を追加し,(最初のfor部分にて)10人分の点数をキーボードから入力し,(次のforの部分にて)画面に表示させましょう.提出は,追加や修正した部分だけで結構です.
課題 11-4
  1. ex11-4.c のプログラムに,複素数の引き算・掛け算・割り算の関数を追加し, それを使った演算部分も追加しましょう.
    提出は,追加した関数部分だけで結構です.
課題 11-5

ex11-5.c のプログラムを変更して,ゲームの面白さを増やしていきましょう.

色々な変更が考えられますが,この講義では以下(省略:元の課題を見て下さい)の変更をしましょう.
できるとこまでで結構ですので,できたプログラムを提出して下さい(項目毎に提出する必要はありません).

今回(第12回)の課題

上記の課題11-1,11-2,11-3,11-4,11-5です.

11-5に関してはできるところまでで結構です.

課題はメールで提出して下さい.

件名はreport11,アドレスはalg01@elec.ryukoku.ac.jp です.