#ifdef GLOBAL_INSTANCEについて

フォーラム(掲示板)ルール
フォーラム(掲示板)ルールはこちら  ※コードを貼り付ける場合は [code][/code] で囲って下さい。詳しくはこちら
kody

#ifdef GLOBAL_INSTANCEについて

#1

投稿記事 by kody » 5年前

失礼します。
C言語を使ってゲームを作りたいと思い、龍神録プログラミングの館からゲームプログラムを作ろうとやり始めた者です。

龍神録プログラミングの館 5章に書かれている GV.hの

コード: 全て選択

//画像用変数宣言部
GLOBAL int img_ch[2][12];	//キャラクタ画像9枚分 X2(変身用)

//構造体変数宣言部
GLOBAL ch_t ch;				//キャラクタデータ宣言
この部分が、なぜグローバル宣言になるのでしょうか?
このソースのどこにも#define GLOBAL_INSTANCEが書かれていないので
疑問に感じました。

これは、main.cppに#define GLOBAL_INSTANCEが書かれているので、
そのソースで呼ばれているGV.hに書かれているから。ということでしょうか?

それですとGV.hに書かれている#include "function.h"でfunction.hが呼ばれているので、
これもGLOBAL_INSTANCEが有効になるのではないのでしょうか?

それとも、#define GLOBAL_INSTANCEが有効なのは呼ばれるmain.cppから呼ばれるヘッダーのみで、そのヘッダーから呼ばれるものは無効ということでしょうか?

この点が良くわからなかったので、すみませんがよろしくお願いいたします。

box
記事: 1688
登録日時: 7年前

Re: #ifdef GLOBAL_INSTANCEについて

#2

投稿記事 by box » 5年前

kody さんが書きました:

コード: 全て選択

//画像用変数宣言部
GLOBAL int img_ch[2][12];	//キャラクタ画像9枚分 X2(変身用)
//構造体変数宣言部
GLOBAL ch_t ch;				//キャラクタデータ宣言
ここの記述と、
kody さんが書きました: このソースのどこにも#define GLOBAL_INSTANCEが書かれていないので
ここの記述との関係が理解できません。
上記のコードのどこにも、GLOBAL_INSTANCEに関する言及はありません。
このソースのどこにも#define GLOBAL が書かれていないので、という記述ならば、まだ理解できます。
_INSTANCE
という記載は、どこから見つけてきたのですか?
バグのないプログラムはない。
プログラムは思ったとおりには動かない。書いたとおりに動く。

beatle
記事: 1280
登録日時: 6年前
住所: 埼玉
連絡を取る:

Re: #ifdef GLOBAL_INSTANCEについて

#3

投稿記事 by beatle » 5年前

externキーワードについて勉強してみてください。

エクレかわああ

Re: #ifdef GLOBAL_INSTANCEについて

#4

投稿記事 by エクレかわああ » 5年前

externについてよく理解していないと思われます。
自分が今いるファイルから見て、その変数がどう見えるかを意識したほうがいいです。
C言語は一つ一つの単純な処理しかできません

naohiro19
記事: 251
登録日時: 7年前
住所: 愛知県

Re: #ifdef GLOBAL_INSTANCEについて

#5

投稿記事 by naohiro19 » 5年前

コード: 全て選択

#ifdef GLOBAL_INSTANCE //もしGLOBAL_INSTANCE を定義していたら 
#define GLOBAL //externが付かないものに置き換える
#else //そうでない場合は
#define GLOBAL extern //extern を付ける
#endif
となります

コード: 全て選択

//a.h
GLOBAL int a; //GLOBAL_INSTANCEを定義していないので extern int a; になる

コード: 全て選択

//a.cpp
#define GLOBAL_INSTANCE
#include "a.h"
GLOAL int a; // GLOBAL_INSTANCEを定義してあるので int a; になる
最後に編集したユーザー naohiro19 on 2012年8月06日(月) 20:40 [ 編集 1 回目 ]

アバター
softya(ソフト屋)
副管理人
記事: 11677
登録日時: 7年前
住所: 東海地方
連絡を取る:

Re: #ifdef GLOBAL_INSTANCEについて

#6

投稿記事 by softya(ソフト屋) » 5年前

5章の最初にGLOBAL_INSTANCEに付いては解説があります。
引用:
#ifdef GLOBAL_INSTANCE
#define GLOBAL
#else
#define GLOBAL extern
#endif
GLOBAL int img;

前半は
「GLOBAL_INSTANCEが定義されていればGLOBALという文字は空白に置き換えられる」
「GLOBAL_INSTANCEが定義されていなければGLOBALという文字はexternに置き換えられる」
という意味です。

そうして
GLOBAL int img;
と書くとどうなるか、そう、externを付けるかつけないか分けることが出来るんです。
main.cppから呼ばれた時は定義しないといけないからexternを付けたくありません。
つまりGLOBAL_INSTANCEを定義してから呼べばいいことになります。

一方他のファイルから呼ばれたときは、externを付けたいです。
つまりGLOBAL_INSTANCEを定義せず、何も書かずに呼べばいいことになります。
ここでメインファイルの一番上を見て下さい。

#define GLOBAL_INSTANCE
#include "../include/GV.h"

を書いてありますね。GLOBAL_INSTANCEを定義してから呼び出しているので、宣言になるのです。
一方他のファイルからは定義されていない状態で呼び出されているので、externが付くのです。
こうして、他のファイルから呼び出せるようになります。
ここの部分が理解できていないなら出る疑問だと思われます。
by softya(ソフト屋) 方針:私は仕組み・考え方を理解して欲しいので直接的なコードを回答することはまれですので、すぐコードがほしい方はその旨をご明記下さい。私以外の方と交代したいと思います(代わりの方がいる保証は出来かねます)。

kody

Re: #ifdef GLOBAL_INSTANCEについて

#7

投稿記事 by kody » 5年前

返答ありがとうございます。
色々調べた結果、どうやら#ifdefやexternではなく、ヘッダファイルの扱いが分かっていない気がします。
そのため引き続き質問させてください。

作った下記 maincpp、GV.h、function.hの3つのファイルをコンパイル→リンクすることを想定したとき
『main.cppファイル』

コード: 全て選択

#define GLOBAL_INSTANCE 
#include "../include/GV.h"

/* ループで必ず行う3大処理 関数 */
int ProcessLoop(){
	if(ProcessMessage()!=0) return -1; //プロセス処理がエラーなら-1を返す
	if(ClearDrawScreen()!=0) return -1; //画面クリア処理がエラーなら-1を返す
	GetHitKeyStateAll_2(); //現在のキー入力処理を行う
	return 0;
}

/* WinMain関数 */
int WINAPI WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance,LPSTR lpCmdLine, int nCmdShow ){
    ChangeWindowMode(TRUE);//ウィンドウモード
    if(DxLib_Init() == -1 || SetDrawScreen( DX_SCREEN_BACK )!=0) return -1; //初期化と裏画面化

	load(); //データロード

	/* このwhile文内でメイン動作 */
	while(ProcessLoop()==0){

		graph_main(); //描画メイン

		if(CheckStateKey(KEY_INPUT_ESCAPE)==1) break; //エスケープが入力されたらブレイク
        ScreenFlip(); //裏画面反映
    }

    DxLib_End(); //DXライブラリ終了処理
    return 0;
}
『GV.hファイル』

コード: 全て選択

#include "../../../include/DxLib.h"
#include "define.h"

#ifdef GLOBAL_INSTANCE
#define GLOBAL			//GLOBAL_INSTANCEが定義されていたらGLOBALを空白に変換
#else
#define GLOBAL extern	//GLOBAL_INSTANCEが定義されていない場合、GLOBALをexternに変換
#endif

#include "function.h"	//関数宣言


//画像用変数宣言部
GLOBAL int img_ch[2][12];	//キャラクタ画像9枚分 X2(変身用)

//構造体変数宣言部
GLOBAL ch_t ch;				//キャラクタデータ宣言
function.h

コード: 全て選択

//graph.cpp
	//描画メイン
	GLOBAL void graph_main();

//key.cpp
	//現在のキー入力処理を行う
	GLOBAL int GetHitKeyStateAll_2();
	//受け取ったキー番号の現在の入力状態を返す
	GLOBAL int CheckStateKey(unsigned char Handle);

//load.cpp
	//データのロード
	GLOBAL void load();
まず、main.cppの2行目にある#include "../include/GV.h"を呼び出して展開する。
『main.cppファイル』

コード: 全て選択

#define GLOBAL_INSTANCE 
#include "../../../include/DxLib.h"
#include "define.h"

#ifdef GLOBAL_INSTANCE
#define GLOBAL			//GLOBAL_INSTANCEが定義されていたらGLOBALを空白に変換
#else
#define GLOBAL extern	//GLOBAL_INSTANCEが定義されていない場合、GLOBALをexternに変換
#endif

#include "function.h"	//関数宣言


//画像用変数宣言部
GLOBAL int img_ch[2][12];	//キャラクタ画像9枚分 X2(変身用)

//構造体変数宣言部
GLOBAL ch_t ch;				//キャラクタデータ宣言

/* ループで必ず行う3大処理 関数 */
int ProcessLoop(){
	if(ProcessMessage()!=0) return -1; //プロセス処理がエラーなら-1を返す
	if(ClearDrawScreen()!=0) return -1; //画面クリア処理がエラーなら-1を返す
	GetHitKeyStateAll_2(); //現在のキー入力処理を行う
	return 0;
}
---以下略---
ここでmain.cppに『#include "function.h" //関数宣言』という記述があるので、
function.hをmain.cppに呼び出す。

『main.cppファイル』

コード: 全て選択

#define GLOBAL_INSTANCE 
#include "../../../include/DxLib.h"
#include "define.h"

#ifdef GLOBAL_INSTANCE
#define GLOBAL			//GLOBAL_INSTANCEが定義されていたらGLOBALを空白に変換
#else
#define GLOBAL extern	//GLOBAL_INSTANCEが定義されていない場合、GLOBALをexternに変換
#endif

//graph.cpp
	//描画メイン
	GLOBAL void graph_main();

//key.cpp
	//現在のキー入力処理を行う
	GLOBAL int GetHitKeyStateAll_2();
	//受け取ったキー番号の現在の入力状態を返す
	GLOBAL int CheckStateKey(unsigned char Handle);

//load.cpp
	//データのロード
	GLOBAL void load();

//画像用変数宣言部
GLOBAL int img_ch[2][12];	//キャラクタ画像9枚分 X2(変身用)

//構造体変数宣言部
GLOBAL ch_t ch;				//キャラクタデータ宣言

/* ループで必ず行う3大処理 関数 */
int ProcessLoop(){
	if(ProcessMessage()!=0) return -1; //プロセス処理がエラーなら-1を返す
	if(ClearDrawScreen()!=0) return -1; //画面クリア処理がエラーなら-1を返す
	GetHitKeyStateAll_2(); //現在のキー入力処理を行う
	return 0;
}
---以下略---
次に以下の記述を実行する

コード: 全て選択

#ifdef GLOBAL_INSTANCE
#define GLOBAL			//GLOBAL_INSTANCEが定義されていたらGLOBALを空白に変換
#else
#define GLOBAL extern	//GLOBAL_INSTANCEが定義されていない場合、GLOBALをexternに変換
#endif
*補足:#include "define.h"は、GLOBALの記述がないファイルなので省略

このときに、main.cppの1行目に、『#define GLOBAL_INSTANCE 』という記述があるため、
GLOBALは、全て空白になってしまうのではないのでしょうか?

「龍神録プログラミングの館」の第4章までは、外部ファイルに記述されている関数を使用するために、
外部関数をmain.cppに呼び出す際に、externを記述するのは分かっていたのですが。

第5章でヘッダファイルの登場で上記の解釈になったしまい、どう展開されているのかがいまいち理解出来ない次第です。
申し訳ありませんが、どの認識が間違っているのかご指摘いただきますようお願いします。

beatle
記事: 1280
登録日時: 6年前
住所: 埼玉
連絡を取る:

Re: #ifdef GLOBAL_INSTANCEについて

#8

投稿記事 by beatle » 5年前

一つ書いておきますと、関数のプロトタイプ宣言(function.hに書いてあるのは全て関数のプロトタイプ宣言です)にはexternを付けても付けなくても意味が変わらないということです。
int hoge;

extern int hoge;
は実体を生成するかしないかにおいて違う意味ですが
void graph_main();

extern void graph_main();
は同じ意味です。どちらも実体を生成するわけではありません。

この知識があれば問題が解決したりしませんか?

beatle
記事: 1280
登録日時: 6年前
住所: 埼玉
連絡を取る:

Re: #ifdef GLOBAL_INSTANCEについて

#9

投稿記事 by beatle » 5年前

今龍神録プログラミングの第5章を見てみましたが、誤解をまねく書き方してありますね。

GV.hでGLOBAL_INSTANCEを使う手法がなぜ効果を発揮するかというと、変数の場合
「externをつけると実体を生成しない」
「付けないと実体を生成する」
という違いがあるからです。

一方で関数プロトタイプ宣言の場合は
「externを付けても付けなくても実体を生成しない」
ので、function.hをGV.hと同じように改造するというのが混乱の元だと思います。

関数の実体はプログラマがどれかのcppファイルに書く必要がありますから、GV.hにあるグローバル変数のように
「GLOBAL_INSTANCEを定義してfunction.hをインクルードしたら関数実体が定義される」
という魔法のようなことは起きないのです。
kody さんが書きました: このときに、main.cppの1行目に、『#define GLOBAL_INSTANCE 』という記述があるため、
GLOBALは、全て空白になってしまうのではないのでしょうか?
はい、正にその通りです。
関数プロトタイプはexternを付けなくても同じなので、問題が発生しないのです。

アバター
softya(ソフト屋)
副管理人
記事: 11677
登録日時: 7年前
住所: 東海地方
連絡を取る:

Re: #ifdef GLOBAL_INSTANCEについて

#10

投稿記事 by softya(ソフト屋) » 5年前

GLOBAL の役目は実体を定義するか参照を宣言するかを切り替えることにあります。
GLOBALが空白なら変数は実体を定義してメモリ空間を取ります。
それに対してGLOBALがexternなら変数の外部参照を宣言して何処か別のソースにある変数の実体とリンクすることを宣言します。
この切り替えを行うのがGLOBAL_INSTANCEの役目です。
ただ関数に関してはbeatleさんの言われる通りプロトタイプ宣言ですからexternの有無は関係なくexternがあるものとして処理されます。
by softya(ソフト屋) 方針:私は仕組み・考え方を理解して欲しいので直接的なコードを回答することはまれですので、すぐコードがほしい方はその旨をご明記下さい。私以外の方と交代したいと思います(代わりの方がいる保証は出来かねます)。

kody

Re: #ifdef GLOBAL_INSTANCEについて

#11

投稿記事 by kody » 5年前

beatle さんが書きました: 「externを付けても付けなくても実体を生成しない」
softya(ソフト屋) さんが書きました: ただ関数に関してはbeatleさんの言われる通りプロトタイプ宣言ですからexternの有無は関係なくexternがあるものとして処理されます。
なるほど良くわかりました。
extern有無に関係がないということで、納得しました。 ありがとうございます。
また何かありましたらご指摘のほど、よろしくお願いいたします。

閉鎖

“C言語何でも質問掲示板” へ戻る