第10章 構造体

1 構造体とは

教科書P158〜159

1-1 構造体とは

異なるデータ型をひとかたまりにして扱うためのデータ型。
分かりやすいプログラムを作ることができる。

[例] キャラクタデータを扱う

画面上(640×480)を歩き回り、敵と3回衝突したらゲームオーバー・・・

《今までのプログラム》
#include     <stdio.h>
#include     <string.h>

void main()
{
    char    gamename[80]; /* ゲームの名前 */
    char    my_charname[20]; /* マイキャラの名前 */
    int     my_x, my_y;         /* マイキャラの座標 */
    int     my_move_x, my_move_y;  /* マイキャラの移動量 */
    int     my_life;         /* マイキャラのライフ */
    int     my_attack_point;  /* マイキャラの攻撃力 */
    char    e_charname[20]; /* 敵キャラの名前 */
    int     e_x, e_y;         /* 敵キャラの座標 */
    int     e_move_x, e_move_y;  /* 敵キャラの移動量 */
    int     e_life;         /* 敵キャラのライフ */
    int     e_attack_point;  /* 敵キャラの攻撃力 */
    int     i, j, k;

    /* キャラクタ情報初期化 */
    strcpy(my_charname, "MyChar");
    my_x = 1;
    my_y = 1;
    my_move_x = 2;
    my_move_y = 2;
    my_life = 3;
    my_attack_point = 5;
    strcpy(e_charname, "ENEMY1");
    e_x = 1;
    e_y = 1;
    e_move_x = 2;
    e_move_y = 2;
    e_life = 3;
    e_attack_point = 5;

    /* 移動処理 */
    ・
    ・
    ・
《構造体を使ったプログラム》
#include     <stdio.h>
#include     <string.h>

void main()
{
    /* 構造体宣言 */
    struct STATUS {
        char    name[20];        /* キャラの名前 */
        int     x, y;            /* マイキャラの座標 */
        int     move_x, move_y;  /* マイキャラの移動量 */
        int     life;            /* マイキャラのライフ */
        int     attack_point;    /* マイキャラの攻撃力 */
    };

    /* 変数宣言 */
    char gamename[80];     /* ゲームの名前 */
    struct STATUS mychar;  /* マイキャラ情報 */
    struct STATUS enemy;   /* 敵キャラ情報 */
    int     i, j, k;

    /* キャラクタ情報初期化 */
    strcpy(mychar.name, "MyChar");
    mychar.x = 1;
    mychar.y = 1;
    mychar.move_x = 2;
    mychar.move_y = 2;
    mychar.life = 3;
    mychar.attack_point = 5;
    strcpy(enemy.name, "ENEMY1");
    enemy.x = 1;
    enemy.y = 1;
    enemy.move_x = 2;
    enemy.move_y = 2;
    enemy.life = 3;
    enemy.attack_point = 5;

    /* 移動処理 */
    ・
    ・
    ・

1-2 構造体の宣言方法

[構文]

struct タグ名 {
 データ型 メンバ名;
 データ型 メンバ名;
  ・
  ・
  ・
};

[例]

struct STATUS {
    char    name[20];        /* キャラの名前 */
    int     x, y;            /* マイキャラの座標 */
    int     move_x, move_y;  /* マイキャラの移動量 */
    int     life;            /* マイキャラのライフ */
    int     attack_point;    /* マイキャラの攻撃力 */
};

1-3 構造体変数の宣言方法

構造体を利用するには、構造体変数を作成する。例えばint型変数aというように、 作成した構造体の型を持つ変数を宣言する。

[構文]

struct タグ名 構造体変数名;

[例]

struct STATUS mychar; /* マイキャラ情報 */

1-4 構造体変数の使い方

構造体変数の各メンバを参照するには、次のように「構造体変数名」と「メンバ名」の間にピリオドを入れる。

[構文]

構造体変数名.メンバ名;

[例]

1.値を代入する
mychar.x = 1;
strcpy(mychar.name, "MyCHAR");
2.出力する
printf("x=%d y=%d\n", mychar.x, mychar.y);

1-5 構造体と構造体変数を同時に宣言する

構造体宣言時に、構造体変数を宣言することができる。

[構文]

struct タグ名 {
 メンバ名 データ型;
 メンバ名 データ型;
  ・
  ・
  ・
} 構造体変数名;

[例]

struct STATUS {
    char    name[20];        /* キャラの名前 */
    int     x, y;            /* マイキャラの座標 */
    int     move_x, move_y;  /* マイキャラの移動量 */
    int     life;            /* マイキャラのライフ */
    int     attack_point;    /* マイキャラの攻撃力 */
} mychar, enemy;

練習問題

教科書159ページ問10

1-6 構造体・構造体変数宣言時のタグ名の省略

構造体宣言時に構造体変数も定義する場合、タグ名を省略して書くことができる。しかしその場合、構造体宣言後に別の構造体変数を宣言することはできない。

[例]

struct {   ←タグ名が省略されている
    char    name[20];        /* キャラの名前 */
    int     x, y;            /* マイキャラの座標 */
    int     move_x, move_y;  /* マイキャラの移動量 */
    int     life;            /* マイキャラのライフ */
    int     attack_point;    /* マイキャラの攻撃力 */
}mychar;

struct 《タグ名?》 enemy;  ←タグ名がないため、構造体変数が宣言できない

2 一括代入

教科書P162〜163

2-1 一括代入

同じ構造体の型を持つ構造体変数同士で値をやり取りする場合、値を一括して代入することができる。

[通常の値の代入]

struct STATUS {
    char    name[20];        /* キャラの名前 */
    int     x, y;            /* マイキャラの座標 */
    int     move_x, move_y;  /* マイキャラの移動量 */
    int     life;            /* マイキャラのライフ */
    int     attack_point;    /* マイキャラの攻撃力 */
}char1, char2;
    ・
    ・
    ・
/* char1のデータをchar2にコピーする */
strcpy(char2.name, char1.name);
char2.x = char1.x;
char2.y = char1.y;
char2.move_x = char1.move_x;
char2.move_y = char1.move_y;
char2.life = char1.life;
char2.attack_point = char1.attack_point;
    ・
    ・
    ・

[一括代入]

struct STATUS {
    char    name[20];        /* キャラの名前 */
    int     x, y;            /* マイキャラの座標 */
    int     move_x, move_y;  /* マイキャラの移動量 */
    int     life;            /* マイキャラのライフ */
    int     attack_point;    /* マイキャラの攻撃力 */
}char1, char2;
    ・
    ・
    ・

/* char1のデータをchar2にコピーする */
char2 = char1;
    ・
    ・
    ・

※構造体渡しのメリットは?

構造体のメンバを追加したとき、代入文を変更する必要が無い!

3 構造体のネスト

教科書補足

3-1 構造体のネスト

構造体の中に、別の構造体を持つことができる。

/* 構造体の宣言 */
struct DSP {          /* 座標 */
    int     x, y;
};
struct STATUS {       /* ステータス */
    char    name[20];                 /* キャラの名前 */
    struct DSP dsp; /* キャラの座標 */
    struct DSP move;/* キャラの移動量 */
    int     life;                     /* キャラのライフ */
    int     attack_point;             /* キャラの攻撃力 */
};

/* 変数の宣言 */
struct STATUS mychar, myshot;
    ・
    ・
    ・
/* 弾を打つ */
myshot.dsp = mychar.dsp;
myshot.move.x = 5;
myshot.move.y = 0;
myshot.life = 1;
    ・
    ・
    ・

4 構造体配列

教科書P160〜161

4-1 構造体配列とは

構造体変数も普通の変数と同様、配列で宣言できる。

/* 構造体宣言 */
struct STATUS {
    char    name[20];        /* キャラの名前 */
    int     x, y;            /* キャラの座標 */
    int     move_x, move_y;  /* キャラの移動量 */
    int     life;            /* キャラのライフ */
    int     attack_point;    /* キャラの攻撃力 */
};

/* 変数宣言 */
struct STATUS mychar;  /* マイキャラ情報 */
struct STATUS enemy[50];   /* 敵キャラ情報(最大50匹) */
    ・
    ・
    ・

[使い方]

構造体変数の配列も、普通の配列と同様、指標を使って利用する。

/* 敵情報の初期化 */
for (i=0 ; i<50 ; i++)
{
    enemy[i].life = 1;
    enemy[i].attack_point = 10;
}

※enemy.life[i]ではないことに注意!!

練習問題

  1. 教科書161ページ練習問題37
  2. 教科書163ページ練習問題38

5 構造体ポインタ

教科書P164〜165

5-1 構造体ポインタ

構造体変数は通常の変数のように宣言することはもちろん、配列にすることもできる。であれば当然ポインタでも宣言できる。

/* 構造体宣言 */
struct STATUS {
    char    name[20];        /* キャラの名前 */
    int     x, y;            /* キャラの座標 */
    int     move_x, move_y;  /* キャラの移動量 */
    int     life;            /* キャラのライフ */
    int     attack_point;    /* キャラの攻撃力 */
};

/* 変数宣言 */
struct STATUS mychar;  /* マイキャラ情報(普通の構造体変数) */
struct STATUS *pmychar;  /* マイキャラ情報(ポインタ型の構造体変数) */
    ・
    ・
    ・

[使い方]

/* 座標の初期化 */
mychar.x = 0;
mychar.y = 0;

/* 右に10ドット移動 */
mychar.x += 10;

/* 座標を表示 */
printf("(%d, %d)\n", mychar.x, mychar.y);

/* 構造体変数mycharのアドレスを構造体ポインタ変数pmycharに代入(格納) */
pmychar = &mychar;

/* 構造体ポインタ変数で座標を表示 */
printf("(%d, %d)\n", mychar->x, mychar->y);

[アロー演算子]

※構造体ポインタ変数の値を参照するには*pmychar.xではなく、pmychar->xである。

[書式]

ポインタ変数名 −> メンバ名

5-2 構造体配列のデータを構造体ポインタで扱う

配列の先頭アドレスをポインタ変数に格納し、配列内データをポインタによって参照することは今までもやってきた。構造体でももちろんできる。

/* 構造体宣言 */
struct STATUS {
    char    name[20];        /* キャラの名前 */
    int     x, y;            /* キャラの座標 */
    int     move_x, move_y;  /* キャラの移動量 */
    int     life;            /* キャラのライフ */
    int     attack_point;    /* キャラの攻撃力 */
};

/* 変数宣言 */
struct STATUS enemy[ENEMY_MAX];  /* 敵キャラ情報(普通の構造体配列) */
struct STATUS *penemy;  /* 敵キャラ情報(ポインタ型の構造体変数) */
int i;

/* ライフの初期化(構造体配列使用時) */
for (i=0 ; i<ENEMY_MAX ; i++)
    enemy[i].life = 0;

/* ライフの初期化(構造体ポインタ使用時) */
penemy = enemy; /* 構造体配列の先頭アドレスを格納(配列なので&は付かない) */
for (i=0 ; i<ENEMY_MAX ; i++)
{
    penemy->life = 0;
    penemy++; /* アドレスを構造体の長さ分移動 */
}

/* ライフの初期化(構造体ポインタ使用時・2) */
penemy = enemy; /* 構造体配列の先頭アドレスを格納 */
for (i=0 ; i<ENEMY_MAX ; i++)
    (penemy + i)->life = 0; /* penemyに格納されているアドレス+構造体の長さ */

/* ライフの初期化(構造体ポインタ使用時・3) */
penemy = enemy; /* 構造体配列の先頭アドレスを格納 */
for (i=0 ; i<ENEMY_MAX ; i++)
    penemy[i]->life = 0; /* 構造体ポインタを配列のように扱う(2よりも分かりやすい?) */

確認

  1. 教科書165ページ例題39

6 構造体変数を関数に渡す

教科書P166〜167

6-1 構造体を宣言する場所

今までは構造体を関数の中に宣言していたが、同じ構造体の型を他の関数でも使いたい場合、関数の外に宣言することにより、関数ごとに宣言する必要がなくなる。

#include     <stdio.h>

#define NAMEMAX 20

/* 構造体宣言 */
struct STATUS {
    char    name[NAMEMAX];   /* キャラの名前 */
    int     x, y;            /* キャラの座標 */
    int     move_x, move_y;  /* キャラの移動量 */
    int     life;            /* キャラのライフ */
    int     attack_point;    /* キャラの攻撃力 */
};

/* プロトタイプ宣言 */
int sub1(void);

void main(void)
{
    struct STATUS status;
    ・
    ・
    ・
}

int sub1(int x)
{
    struct STATUS mychar;
    ・
    ・
    ・
}

6-2 構造体変数を関数に渡す

普通の変数と同様、構造体変数も関数に渡すことができる。

[プログラム]

#include <stdio.h>

struct STATUS {
    int x, y;
};

void sub(struct STATUS);

void main(void)
{
    struct STATUS status;

    status.x = 0;
    status.y = 0;

    printf("main関数からの(%d, %d)\n", status.x, status.y);
    sub(status);
    printf("main関数からの(%d, %d)\n", status.x, status.y);

}

void sub(struct STATUS s)
{
    s.x = 10;
    printf("sub関数からの(%d, %d)\n", s.x, s.y);

}

[実行結果]

main関数からの(0, 0)
sub関数からの(10, 0)
main関数からの(0, 0)

[POINT]

main関数の構造体変数をsub関数に渡しているが引数はアドレスではないため、sub関数内でデータを操作してもmain関数内のデータには影響がない。

6-3 関数の引数に構造体ポインタを使う

構造体変数のデータを関数で操作したい場合、引数に構造体ポインタを使用する。

[プログラム]

#include <stdio.h>

struct STATUS {
    int x, y;
};

void sub(struct STATUS *);

void main(void)
{
    struct STATUS status;

    status.x = 0;
    status.y = 0;

    printf("main関数からの(%d, %d)\n", status.x, status.y);
    sub(&status);
    printf("main関数からの(%d, %d)\n", status.x, status.y);

}

void sub(struct STATUS *s)
{
    s->x = 10;
    printf("sub関数からの(%d, %d)\n", s->x, s->y);

}

[実行結果]

main関数からの(0, 0)
sub関数からの(10, 0)
main関数からの(10, 0)

[POINT]

main関数の構造体変数のアドレスをsub関数に渡しているため、sub関数内でデータを操作するとmain関数内のデータにそのまま反映される。

練習問題

  1. 教科書166ページ練習問題39

7 構造体をデータ型のように扱う

教科書にはない

7-1 構造体をデータ型のように扱う

構造体変数の宣言を、普通の変数のように宣言する方法を紹介する。

[プログラム]

#include <stdio.h>

typedef struct _STATUS {
    int x, y;
} STATUS;

void sub(STATUS *);

void main(void)
{
    STATUS status;

    status.x = 0;
    status.y = 0;

    printf("main関数からの(%d, %d)\n", status.x, status.y);
    sub(&status);
    printf("main関数からの(%d, %d)\n", status.x, status.y);

}

void sub(STATUS *s)
{
    s->x = 10;
    printf("sub関数からの(%d, %d)\n", s->x, s->y);

}

7-2 typedef

既に定義されている型指定子に対し、別の名前を割り付ける型指定子。関数内では定義できない

[書式]

typedef 既に定義されている型識別子 別名;

[使用例]

#incluede     <stdio.h>

typedef int INT;

void main(void)
{
    INT i; /* int i;と同じ意味になる */
    i = 0;
    ・
    ・
    ・

[使用例2:ポインタ]

#include     <stdio.h>

typedef int *PINT;

void main(void)
{
    int a;
    PINT pa; /* int *pa;と同じ意味になる */
    pa = &a;
    *pa = 0;
    ・
    ・
    ・

[使用例3:構造体・構造体ポインタ]

#include <stdio.h>

struct _STATUS {
    int x, y;
};
typedef struct _STATUS STATUS; /* 構造体 */
typedef struct _STATUS *PSTATUS; /* 構造体ポインタ */

void sub1(STATUS);
void sub2(PSTATUS);

void main(void)
{
    STATUS status;

    status.x = 0;
    status.y = 0;

    sub1(status);
    sub2(&status);
    ・
    ・
    ・
}

void sub1(STATUS s)
{
    s.x = 0;
    ・
    ・
    ・
}

void sub2(PSTATUS ps)
{
    s->x = 0;
    ・
    ・
    ・
}

[使用例4:構造体宣言時に別名もつける]

#include 

typedef struct _STATUS {
    int x, y;
} STATUS, *PSTATUS;

※構造体宣言時に構造体変数を宣言するのと同じ形式だが、先頭にtypedefが付いているため、「構造体変数」ではなく「構造体の別名」になることに注意!!


[ TOP ]