教科書P158〜159
異なるデータ型を
ひとかたまりにして扱うためのデータ型。
分かりやすいプログラムを作ることができる。[例] キャラクタデータを扱う
画面上(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; /* 移動処理 */ ・ ・ ・
[構文]
struct タグ名 {
データ型 メンバ名;
データ型 メンバ名;
・
・
・
};[例]
struct STATUS { char name[20]; /* キャラの名前 */ int x, y; /* マイキャラの座標 */ int move_x, move_y; /* マイキャラの移動量 */ int life; /* マイキャラのライフ */ int attack_point; /* マイキャラの攻撃力 */ };
構造体を利用するには、構造体変数を作成する。例えばint型変数aというように、 作成した構造体の型を持つ変数を宣言する。
[構文]
struct タグ名 構造体変数名; [例]
struct STATUS mychar; /* マイキャラ情報 */
構造体変数の各メンバを参照するには、次のように「構造体変数名」と「メンバ名」の間にピリオドを入れる。
[構文]
構造体変数名.メンバ名; [例]
- 1.値を代入する
mychar.x = 1;strcpy(mychar.name, "MyCHAR");- 2.出力する
printf("x=%d y=%d\n", mychar.x, mychar.y);
構造体宣言時に、構造体変数を宣言することができる。
[構文]
struct タグ名 {
メンバ名 データ型;
メンバ名 データ型;
・
・
・
}構造体変数名;[例]
struct STATUS { char name[20]; /* キャラの名前 */ int x, y; /* マイキャラの座標 */ int move_x, move_y; /* マイキャラの移動量 */ int life; /* マイキャラのライフ */ int attack_point; /* マイキャラの攻撃力 */ }mychar, enemy;練習問題
教科書159ページ問10
構造体宣言時に構造体変数も定義する場合、タグ名を省略して書くことができる。しかしその場合、構造体宣言後に別の構造体変数を宣言することはできない。
[例]
struct { ←タグ名が省略されているchar name[20]; /* キャラの名前 */ int x, y; /* マイキャラの座標 */ int move_x, move_y; /* マイキャラの移動量 */ int life; /* マイキャラのライフ */ int attack_point; /* マイキャラの攻撃力 */ }mychar; struct《タグ名?》enemy; ←タグ名がないため、構造体変数が宣言できない
教科書P162〜163
同じ構造体の型を持つ構造体変数同士で値をやり取りする場合、値を一括して代入することができる。
[通常の値の代入]
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;・ ・ ・※構造体渡しのメリットは?
構造体のメンバを追加したとき、代入文を変更する必要が無い!
教科書補足
構造体の中に、別の構造体を持つことができる。
/* 構造体の宣言 */ 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; ・ ・ ・
教科書P160〜161
構造体変数も普通の変数と同様、配列で宣言できる。
/* 構造体宣言 */ struct STATUS { char name[20]; /* キャラの名前 */ int x, y; /* キャラの座標 */ int move_x, move_y; /* キャラの移動量 */ int life; /* キャラのライフ */ int attack_point; /* キャラの攻撃力 */ }; /* 変数宣言 */ struct STATUS mychar; /* マイキャラ情報 */ struct STATUSenemy[50]; /* 敵キャラ情報(最大50匹) */ ・ ・ ・[使い方]
構造体変数の配列も、普通の配列と同様、指標を使って利用する。
/* 敵情報の初期化 */ for (i=0 ; i<50 ; i++) { enemy[i].life = 1; enemy[i].attack_point = 10; }※enemy.life[i]ではないことに注意!!
練習問題
- 教科書161ページ練習問題37
- 教科書163ページ練習問題38
教科書P164〜165
構造体変数は通常の変数のように宣言することはもちろん、配列にすることもできる。であれば当然ポインタでも宣言できる。
/* 構造体宣言 */ 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である。[書式]
ポインタ変数名 −> メンバ名
配列の先頭アドレスをポインタ変数に格納し、配列内データをポインタによって参照することは今までもやってきた。構造体でももちろんできる。
/* 構造体宣言 */ 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よりも分かりやすい?) */確認
- 教科書165ページ例題39
教科書P166〜167
今までは構造体を関数の中に宣言していたが、同じ構造体の型を他の関数でも使いたい場合、関数の外に宣言することにより、関数ごとに宣言する必要がなくなる。
#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;・ ・ ・ }
普通の変数と同様、構造体変数も関数に渡すことができる。
[プログラム]
#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関数内のデータには影響がない。
構造体変数のデータを関数で操作したい場合、引数に構造体ポインタを使用する。
[プログラム]
#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関数内のデータにそのまま反映される。練習問題
- 教科書166ページ練習問題39
教科書にはない
構造体変数の宣言を、普通の変数のように宣言する方法を紹介する。
[プログラム]
#include <stdio.h>typedefstruct _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); }
既に定義されている型指定子に対し、
別の名前を割り付ける型指定子。関数内では定義できない。[書式]
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 typedefstruct _STATUS { int x, y; }STATUS, *PSTATUS;※構造体宣言時に構造体変数を宣言するのと同じ形式だが、先頭にtypedefが付いているため、「構造体変数」ではなく「構造体の別名」になることに注意!!