ページ 11

グローバル変数

Posted: 2010年10月09日(土) 17:45
by 笹身
こんばんは、笹身という者です。

今回はグローバル変数について、質問があるので質問掲示板にて、質問させて頂きます。

本題ですが、グローバル変数は、使わないほうがいい、危険性がある。などと言われているので
なるべくグローバル変数の使用を抑えようとしているのですが、
今回の場合、グローバル変数を使わないでどのように表現したらよいでしょうか?

開発環境:VC++
言語:C言語

ソースコード

"test1.h"

//プレイヤーデータ
typedef struct Player{
int Graph[12]; //プレイヤー画像の取得
int X; //プレイヤー座標Xの取得
int Y; //プレイヤー座標Yの取得
int direction; //プレイヤーの向き(9=上 0=下 6=右 3=左)
int walk_flag; //プレイヤーの移動状態(止まっているか動いているか)
}Player;

test2.cpp
void PLAYER_walking(){

//構造体の初期化
Player Player; //プレイヤーのデータ(画像、座標(XY)向き)
Player.direction; //向き
Player.X = PLAYER_X;//X座標
Player.Y = PLAYER_Y;//Y座標
Player.walk_flag =0;//移動状態(1=ON)

//数変数群
LoadDivGraph( "test.png" , 12 , 3 , 4 , 32 , 32 , Player.Graph );//プレイヤー画像
.
.
.

void Barret_data(){
ここにプレイヤーの座標と向きを元に弾を表示させたいのですが、
     もう一度構造体を初期化(?)して、向き、座標を取得しないといけないのでしょうか?

グローバル変数を使うと簡単に取得できるのですが、使わないほうがいいと言われているので
なるべく使いたくないです。

コードが分かりにくかったら申しわけございません。

Re:グローバル変数

Posted: 2010年10月09日(土) 17:54
by みけCAT
コード全体を見ないとよくわかりませんが、
関数の引数でポインタを渡すというのはどうですか?
構造体のポインタのメンバへのアクセスは.のかわりに->を使います。
変数のポインタを取得するには、変数の前に&をつけます。

Re:グローバル変数

Posted: 2010年10月09日(土) 18:18
by Dixq (管理人)
グローバル変数は安全に使えば非常に有用です。
むしろCで大きなプログラムを書くならモジュールごとに共通に使える変数は用意するべきでしょう。

C++ではクラスのメンバ変数として複数の関数間で共通に使う変数が用意出来ますが、
Cではそれが出来ません。しかしファイルごとにファイルの先頭にstatic変数を用意すれば似たような事が出来ます。
複数のファイル間で共通して使えるグローバル変数は0にし(全てのグローバル変数はstatic)、
値の取得はそのファイルに書いたGet関数を通して得るようにします。

そうすることで危険性は減らせます。

例えば、自機モジュールとそれを管理するメインモジュールがあったとしましょう。

自機.hにはメインモジュールから呼び出す関数のプロトタイプ宣言を書きます。
自機.cにはまず先頭に、上のプログラムで言う

statict Player_t Player;

を書き、Player自機.cのファイルの中ではどの関数からでもアクセスできるようにします。
(ただし、staticが付いているので、このファイルの外からはアクセスできません。)
自機の処理をする関数を自機.cに書きます。

プレイヤーの何らかの値が欲しい場合、例えばプレイヤーの座標を取得するような関数を使いたい時は

void GetPlayerPos( int *x, int *y ){
*x = Player.x;
*y = Player.y;
}

のような関数を通して渡してあげます。
そうすれば、元の変数の値が書き換わる可能性がありません。

Re:グローバル変数

Posted: 2010年10月09日(土) 18:32
by Dixq (管理人)
goto文もグローバル変数も同じことが言えます。
C言語を学び始めた人には「なるべく使わないように」と教えると思います。
それは良く理解せずに使うと非効率になりがちだからです。
でもgoto文もグローバル変数も正しく使えば非常に有用です。

例えばgoto文で言えば、深いループから抜ける時や、終了処理をしてから関数を抜けたい時など、
goto文を使った方がスマートになる場合があります。

また、グローバル変数の使い方の例として以下のようなプログラムを書いてみました。
完全にモジュールを切り分ける単位として解りやすいモジュールとしてUIがあるでしょう。
ここではキーボードをUIとしています。

/*** UI.h ***/

// キーの入力状態を計算する
int UI_CalcHitKeyStateAll();

// KeyNumberのボタンの入力状態を返す
int UI_GetHitButton( unsigned char KeyNumber );

/*************************************************/

/*** UI.cpp ***/

#include "DxLib.h"

static int gKey[ 256 ]; // キー入力状態を格納するための int型配列

// キーの入力状態を計算する
// 返り値 0:正常
int UI_CalcHitKeyStateAll(){
int i;
char KeyInputNow[256]; // 現在のキーの入力状態を格納する配列
GetHitKeyStateAll( KeyInputNow ); // 現在のキーの入力状態を取得
for( i=0; i<256; i++ ){
if( KeyInputNow == 1 ){ // i番目のキーが押されていたら
gKey++; // i番目の配列要素の値を加算
} else { // i番目のキーが押されていなかったら
gKey = 0; // 要素の中身を0に
}
}
return 0;
}

// KeyNumberのボタンの入力状態を返す
// 返り値 自然数:KeyNumberのキーの入力状態
int UI_GetHitButton( unsigned char KeyNumber ){
return gKey[ KeyNumber ];
}
/*************************************************/

このプログラムはゲームプログラミングの館のGetHitKeyStateAll_2をゲームのモジュールに適用したものです。
どのキーが現在おされているかという情報はあらゆるモジュールが必要とします。
しかし、どこからでもその情報が書かれている変数にアクセス出来てしまうと、安全性が下がります。
そこで、変数に直接アクセスすることなく値を取得する為に、UI_GetHitButton関数 を利用します。

こうすることで安全にグローバル変数の値を取得できます。
また、グローバル変数の値が書き換わるタイミングがわけわからないなんてことにもなりません。

キーボードの入力状態を取得したいファイルの先頭に

#include "UI.h"

を書きます。すると、そのファイルからこの関数が呼べるようになります。
全てのモジュールにおいてこの要領で作っていけばかなり大きな構造でも対応出来そうです。
ただやはりC++よりは非効率になる事が多いですが・・。

Re:グローバル変数

Posted: 2010年10月09日(土) 19:53
by 笹身
返信ありがとうございます。

>みけCAT様
引数を使う方法ですが一応考えたのですがどの部分で値を渡せばいいのか分からなくて
断念しました。

>Dixq (管理人)様
グローバル変数、goto文の有用性を丁寧に解説してくださり、ありがとうございます。
今回は、他の関数(当たり判定など)でも、プレイヤーの情報を必要になると思ったので
グローバル変数を使ってみます。

忙しいなかご返信ありがとうございます。

Re:グローバル変数

Posted: 2010年10月09日(土) 19:53
by 笹身
解決