★構造体(基礎1)★


今回は新しい考え方「構造体」の説明をします。

ずいぶん便利な仕組みなのですが、ここでつまづく方もいらっしゃるようです。

何が引っ掛かるのかよく分かりませんが、使い道が不明なのではないでしょうか?

使い道が分かれば、これほど便利な仕組みは無いと思います。

また、C++のクラスにも通じる考え方なので、非常に重要な項目だと思います。


「構造体」とは何をするものなのかというと、関連のあるデータをまとめる仕組みです。

データというのは、これまで使ってきた「変数」や「配列」などを指します。

複数のデータを1つにまとめることが出来るため、所属を明らかにすることが出来ます。

また、ずっと後に説明する関数でも威力を発揮します。


では、基本的な使い方について説明します。

※まずは文法のみ説明して、具体例は後から説明します。

基本的な文法は↓の通りです。

<構造体宣言>

struct タグ名
{
    メンバ変数宣言;
    メンバ変数宣言;
      ・
      ・
      ・
};

タグ名というのは「構造体」につける名前です。

メンバ変数というのは「変数」や「配列」のことで、ここで複数の「変数」や「配列」を宣言出来ます。

まずは、新しくこのような「構造体」を作ることを「宣言」します。

※「宣言」だけでは使えないので注意してください。

<構造体宣言の例>

struct Sample
{
    int a;
    int b;
    char c[10];
};

実際にプログラムで使う際には「実体(インスタンス)」が必要となります。

「実体(インスタンス)」の作り方は↓の通りです。

<実体の作成>

struct タグ名 実体名;

これで実際に使える「構造体」が出来上がります。

<実体作成の例>

struct Sample sample;

※sampleが実体として使用する名前です。

※タグ名と同じ名前にしなくても良いです。


実際のデータは「メンバ変数」に入っていますので、「メンバ変数」へのアクセス方法を示します。

例としてメンバ変数に0を代入するケースを書きます。

<メンバ変数へのアクセス>

実体名.メンバ変数名 = 0;

メンバ変数へのアクセスには「.(ドット演算子)」を使います。

<メンバ変数アクセスの例>

sample.a = 0;

と書くと、実体sampleに所属しているaという変数に0を代入することになります。


これだけでは、まだまだ説明不足ですので、実際にプログラムの例を書いて説明します。


まとめるデータとして以下のものを用意しました。

ゲームのキャラクターデータのようなものです。

 キャラクター名 char name[20];
 ヒットポイント int hp
 攻撃力         int attack
 所持金         int gold

タグ名は「Character」にしましょう。

実体名は主人公を表す「hero」にします。

宣言を書き、実体を作り、データを代入するところまで書いてみます。


<sample program 100-01>

#include <stdio.h>

#define MAX_NAME 20

/* 構造体宣言 */

struct Character
{
    char name[MAX_NAME];
    int hp;
    int attack;
    int gold;
};

int main(void)
{
    /* 実体の作成 */

    struct Character hero;

    /* メンバ変数へのアクセス */

    scanf("%s", hero.name);

    hero.hp = 100;

    hero.attack = 10;

    hero.gold = 0;

    return 0;
}

ゲームキャラクターのデータを初期化しているイメージですね。

ゲームのキャラクターデータということで、ついでに↓のことも説明します。


「構造体」は別のプログラム言語では「ユーザ定義型」と呼ばれることがあります。

「ユーザ」が自分で「定義」する「型」ということですね。

「型」というのはint型とかdouble型といったデータの入れ物のことです。

そして「構造体」も「ユーザ」が作った1つの型なので、「構造体」の「配列」も作ることが出来ます。


「構造体」の配列は色々な場面で使います。

例えば、上のサンプルでゲームのキャラクターの構造体を作りました。

この構造体を「配列」にするということは、キャラクター数人でパーティを組むというイメージになります。

プログラムを書いてみます。


パーティメンバーは3人ということにして、#defineでPARTYを3に設定します。

構造体の実体名もheroでは1人のイメージなので、playerという名前の配列にします。

メンバ変数へのアクセスも配列の添え字を指定して行います。

さらに表示のプログラムも加えてみました。

<sample program 100-02>

#include <stdio.h>

#define MAX_NAME 20

#define PARTY 3

/* 構造体宣言 */

struct Character
{
    char name[MAX_NAME];
    int hp;
    int attack;
    int gold;
};

int main(void)
{
    /* 実体の作成 */

    struct Character player[PARTY];

    int i;

    /* メンバ変数へのアクセス */

    for (i = 0; i < PARTY; i++) {

        scanf("%s", player[i].name);

        player[i].hp = 100;

        player[i].attack = 10;

        player[i].gold = 0;
    }

    for (i = 0; i < PARTY; i++) {
        printf("%s : HP %d : ATK %d : G %d\n", player[i].name, player[i].hp, player[i].attack, player[i].gold);
    }

    return 0;
}

<実行結果>

player1 ← scanfの入力
player2 ← scanfの入力
player3 ← scanfの入力
player1 : HP 100 : ATK 10 : G 0
player2 : HP 100 : ATK 10 : G 0
player3 : HP 100 : ATK 10 : G 0
続行するには何かキーを押してください・・・

ちなみに上と同じことを「構造体」を使わずに書くとこうなります。

<sample program 100-03>

#include <stdio.h>

#define MAX_NAME 20

#define PARTY 3

int main(void)
{
    char playerName[PARTY][MAX_NAME];

    int playerHp[PARTY];

    int playerAttack[PARTY];

    int playerGold[PARTY];

    int i;

    /* メンバ変数へのアクセス */

    for (i = 0; i < PARTY; i++) {

        scanf("%s", playerName[i]);

        playerHp[i] = 100;

        playerAttack[i] = 10;

        playerGold[i] = 0;
    }

    for (i = 0; i < PARTY; i++) {
        printf("%s : HP %d : ATK %d : G %d\n", playerName[i], playerHp[i], playerAttack[i], playerGold[i]);
    }

    return 0;
}

<実行結果>

player1 ← scanfの入力
player2 ← scanfの入力
player3 ← scanfの入力
player1 : HP 100 : ATK 10 : G 0
player2 : HP 100 : ATK 10 : G 0
player3 : HP 100 : ATK 10 : G 0
続行するには何かキーを押してください・・・

このように、プレイヤーに必要な項目の数だけ配列が必要になり、多くなればなるほど分かりにくくなってきます。

「構造体」にしておくことで、データをまとめて管理出来るので、非常にすっきりと整理出来ます。

トランプの「数字」と「マーク」など、これまでのプログラムでも「構造体」でまとめられそうなものがありました。

これから先は、まとめられるデータは「構造体」でまとめるようにしましょう。


次へ

戻る

目次へ