第16章 構造体 ゲーム作成

第16章
構造体
16.3 関数と構造体
構造体ポインタ
地底探査ゲーム
今日のポイント
構造体、タグ名、メンバーとそれらの
使い方を思い出す!
 構造体のデータを関数間でやりとり
する場合の仮引数・実引数の使い方
 関数値を構造体で戻す方法

16.3 関数と構造体
構造体、タグ名、メンバーとは何だったか?
プログラム例 16.3.2 から
タグ名
struct point {
double x;
double y;
メンバーの宣言
};
x 座標
y 座標
「2つのdouble型から成る構造体に
point という名前のブランドタグをつ
けよう」
という感じ
void center_point(struct point p1, struct point p2);
中点を求めて出力する関数 center_point のプロトタイプ宣言
p1, p2 は point というタグがついた構造体変数
構造体とメンバーのアドレス
#include <stdio.h>
struct point {double x,y;};
int main(void)
{
struct point p1;
同じ型ならまとめて書いても良い
p1
p1.x → 1310568
3.8
p1.y → 1310576
5.6
p1.x = 3.8; p1.y = 5.6;
printf("%g, %g, (%g, %g)\n", p1.x, p1.y, p1);
printf("%u, %u, %u\n", &p1.x, &p1.y, &p1);
return 0;
}
メンバーを省略すると
先頭から順にメンバーの
数だけ出力しようとする
(VC++の場合)
構造体の配列の構造
#include <stdio.h>
struct point {double x,y;};
p[0]
int main(void)
{
struct point p[2];
p[0].x = 3.8; p[0].y =
p[1].x = 7.4; p[1].y =
printf("(%g, %g), (%g,
printf("%u, %u, %u\n",
printf("%u, %u, %u\n",
return 0;
}
p[1]
p[0].x → 1310560
3.8
p[0].y → 1310568
5.6
p[1].x → 1310576
8.4
p[1].y → 1310584
18.2
5.6;
9.2;
%g)\n", p[0].x, p[0].y, p[1].x, p[1].y);
&p[0].x, &p[0].y, &p[0]);
&p[1].x, &p[1].y, &p[1]);
16.3 関数と構造体
プログラム例 16.3.2
構造体データの引渡し
#include <stdio.h>
struct point {double x, y;};
void center_point(struct point p1, struct point p2);
int main(void)
{
struct point p1, p2;
p1.x = 3.8; p1.y = 5.6;
p2.x = 8.4; p2.y = 18.2;
center_point(p1, p2);
return 0;
}
構造体の実引数を関数に引き渡す
16.3 関数と構造体
プログラム例 16.3.2
構造体データの引渡し
仮引数が構造体である関数の定義
void center_point(struct point p1, struct point p2)
{
double xm, ym;
xm = (p1.x +
ym = (p1.y +
printf("(%g,
p1.x,
メンバーを用いて計算
p2.x) / 2.;
p2.y) / 2.;
%g) と (%g, %g) の中点の座標は (%g, %g)\n",
p1.y, p2.x, p2.y, xm, ym);
}
出力はメンバーで
16.3 関数と構造体
プログラム例 16.3.2改
関数から結果を構造体で
受け取る → 構造体ポインタ
#include <stdio.h>
struct point {double x, y;};
void center_point(struct point p1, struct point p2,
struct point *p3);
int main(void)
{
結果を受け取る構造体 p3 を用意
struct point p1, p2, p3;
p1.x = 3.8; p1.y = 5.6;
構造体変数 p3 のアドレスを関数
p2.x = 8.4; p2.y = 18.2;
に引き渡して結果を受け取る
center_point(p1, p2, &p3);
printf("(%g, %g) と (%g, %g) の中点の座標は (%g, %g)\n",
p1.x, p1.y, p2.x, p2.y, p3.x, p3.y);
return 0;
出力はメンバーで
}
16.3 関数と構造体
プログラム例 16.3.2改
関数から結果を構造体で
受け取る → 構造体ポインタ
void center_point(struct point p1, struct point p2,
struct point *p3)
仮引数が構造体ポインタである関数の定義
{
(*p3).x = (p1.x + p2.x) / 2.;
メンバーを用いて計算
(*p3).y = (p1.y + p2.y) / 2.;
}
構造体ポインタの場合、選択演算子(アロー演算子) "->" でメンバーを指定
(*p3).x は p3 -> x に書き換えて p3 -> x = (p1.x + p2.x) / 2.;
(*p3).y
p3 -> y
p3 -> y = (p1.y + p2.y) / 2.;
16.3 関数と構造体
プログラム例 16.3.2改
関数から結果を構造体で
受け取る → 構造体ポインタ
void center_point(struct point p1, struct point p2,
struct point *p3)
仮引数が構造体ポインタである関数の定義
{
p3 -> x = (p1.x + p2.x) / 2.;
メンバーを用いて計算
p3 -> y = (p1.y + p2.y) / 2.;
printf("p3 -> x = %g, p3 -> y = %g\n", p3 -> x, p3 -> y);
printf("(*p3).x = %g, (*p3).y = %g\n", (*p3).x, (*p3).y);
printf("&p3 -> x = %u, &p3 -> y = %u\n", &p3 -> x, &p3 -> y);
printf("&(*p3).x = %u, &(*p3).y = %u\n", &(*p3).x, &(*p3).y);
}
優先順位はポインタ演算子よりもメンバー演算子のほうが高いことに注意
*p3.x は *(p3.x) と解釈されるので、p3 がポインタの場合は間違いとなる。
16.3 関数と構造体
プログラム例 16.3.2改2
関数から結果を構造体で
受け取るもう1つの方法
#include <stdio.h>
struct point {double x, y;};
struct point center_point(struct point p1, struct point p2);
戻り値が構造体であることを宣言
int main(void)
{
結果を受け取る構造体 p3 を用意
struct point p1, p2, p3;
p1.x = 3.8; p1.y = 5.6;
結果の構造体を関数から戻り値で受け取る
p2.x = 8.4; p2.y = 18.2;
p3 = center_point(p1, p2);
printf("(%g, %g) と (%g, %g) の"中点の座標は (%g, %g)\n",
p1.x, p1.y, p2.x, p2.y, p3.x, p3.y);
return 0;
}
出力はメンバーで
16.3 関数と構造体
プログラム例 16.3.2改2
関数から結果を構造体で
受け取るもう1つの方法
戻り値が構造体である関数の定義
struct point center_point(struct point p1, struct point p2)
{
結果を入れる構造体を用意
struct point p;
p.x = (p1.x + p2.x) / 2.;
p.y = (p1.y + p2.y) / 2.;
return p;
メンバーを用いて計算
}
結果を構造体で返す
地底探検ゲーム(undergnd.c)
10
4
■■ ■
■
現在地
■
■
■
ブロック
■
■☆
■
■
■
距離 0 m: 4←→6 ↓=2 ギブアップ=0:
もぐった距離
テンキーで入力
左右にブロックを避けながら下にすすんでいき、進め
なくなったところまでの距離(スコア)を競うゲーム
地底探検ゲーム(undergnd.c)
講義資料のWebページからダウンロード
#include
#include
#include
#include
<stdio.h>
<stdlib.h>
<string.h>
<time.h>
乱数発生に必要
乱数初期化に必要
/* 地面を格納する構造体の宣言 */
struct ground {
int block[10];
// 地下情報を格納する配列の宣言
char blockStr[21]; // 地下ブロックパターン
};
g[0].block = {1, 0, 0, 1, 0, 0, 0, 1, 0, 1}
g[0].blockstr ■__■___■_■
2バイト文字なので倍のサイズが必要 + \0 → 21バイト
地底探検ゲーム(undergnd.c)
/* 地面を作成する関数 */
void makeGround(struct ground *p)
{
int i;
char *wall[] = { " ", "■" };
3バイトの文字列2つの配列
/* 左の壁 */
"■"
p??block[0] = 1;
4で割り切れたら0:偽→1,
strcpy(p??blockStr, wall[1]);
割り切れなければ1~3:真→0
/* 中央のブロックパターン */
for (i = 1; i <= 8; i++) {
p??block[i] = rand() % 4 ? 0 : 1; // 確率1/4でブロック発生
strcat(p??blockStr, wall[p??block[i]]);
}
/* 右の壁 */
対応する位置にブロックを置く
p??block[9] = 1;
strcat(p??blockStr, wall[1]);
"■"
}
地底探検ゲーム(undergnd.c)
現在地記憶用変数
int main(void)
ギブアップチェック用フラグ
{
int i, score = 0, x = 4, key = 1;
struct ground g[4]; // 地面の構造体配列(4層分)
/* 最初の地面の作成 */
srand(time(NULL) % 100); // 乱数シード:現在時刻の100の剰余
for (i = 0; i < 4; i++) makeGround(g + i);
構造体配列の
/* メインループ */ 現在地マーク
ポインタで引渡し
while (key) {
の埋め込み
/* 画面表示 */
strncpy(g[2]?blockStr + x * 2, "☆", 2);
for (i = 0; i < 4; i++)
printf("%s\n", g[i]?blockStr);
4層分の地面を描画
strncpy(g[2]?blockStr + x * 2, " ", 2);
次回の印刷時に前回の☆印が残らないよ
うに2バイト分の空白で事前に消しておく
地底探検ゲーム(undergnd.c)
/* キー入力 */
printf("距離 %d m: ", score);
printf("4←→6 ↓=2 ギブアップ=0: ");
scanf("%d", &key);
左に移動
/* 移動 */
if
(key == 4 && g[2]?block[x - 1] == 0) x--;
右に移動
else if (key == 6 && g[2]?block[x + 1] == 0) x++;
else if (key == 2 && g[3]?block[x] == 0) {
下に移動
for (i = 0; i < 3; i++) g[i] = g[i + 1];
makeGround(g + 3);
1段下から
score++;
順にコピー
最下層の新規作成
}
}
/* 終了処理 */
printf("スコア: %d メートル!\n", score);
return 0;
}
スキルアップタイム1



地底探検ゲーム(undergnd.c)を完成させ、コン
パイル・実行して動作を確認せよ
地面の深さや幅を変えるにはどこを変更すれば
よいか、検討せよ
地面の深さや幅を変えやすいようにソースを改
良し(undergnd1.c)、深さを1、幅を2、増やせ
(4→5, 10→12)。
ただし、現在地は常に下から2層目とする
スキルアップタイム2


ファイル入出力関数の利用により、「ハイスコア
の表示・保存機能」を付け足せ(保存ファイル名
はhighscr.txt)
ソース名は"undergnd2.c"とせよ
スキルアップタイム2のヒント
int highscore=0;
FILE *fp;
ハイスコアの初期値
...
if ((fp = fopen("highscr.txt", "r")) != NULL) {
fscanf(fp, "%d", &highscore);
ハイスコアファイルが
fclose(fp);
ある場合に読み込む
}
if (score > highscore) {
printf("ハイスコアです!¥n");
fp = fopen("highscr.txt", "w");
ハイスコアの場合に
fprintf(fp, "%d¥n", score);
ファイルに書き込む
fclose(fp);
} else printf("ハイスコア: %d メートル¥n", highscore);
第4回レポート(任意)
課題: スキルアップタイム1または2



提出期限: 2010年2月4日(金) 12:50
提出場所: ネットワーク実験室(1)入口の提出箱
以下の項目を入れること(表紙は不要)
1.学籍番号、氏 名
2. "undergnd1.c"または"undergnd2.c"のソース
リスト
3.実行結果の例(変更したことがわかる部分だけ
残し、後は省略してよい)
4.感 想
本日のパズル
このプログラムは何を出力するか
#include <stdio.h>
#define PR(value) printf(#value"=%d ",(value))
#define NL putchar('\n')
#define PRINT3(x1,x2,x3) PR(x1),PR(x2),PR(x3),NL
int a[2][3] = {{1,2,3},{4,5,6}};
int main(void)
{
int i;
for (i=0; i<2; i++)
PRINT3(a[i][2-i], *a[i], *(*(a+i)+i));
return 0;
}