ヘッダファイルでのインクルードについて

フォーラム(掲示板)ルール
フォーラム(掲示板)ルールはこちら  ※コードを貼り付ける場合は [code][/code] で囲って下さい。詳しくはこちら
イマダニ
記事: 145
登録日時: 7年前

ヘッダファイルでのインクルードについて

#1

投稿記事 by イマダニ » 6年前

まずプレイヤーの構造体が定義されてるplayer.hと
武器の構造体が定義されてるwapon.hのふたつのヘッダファイルがあります。

コード:

//player.h

#ifndef DEF_PLAYER_H
#define DEF_PLAYER_H

#include "wapon.h"

typedef struct{
	int py,px;
	int oldy,oldx;
	bool walkflag;
.
.
.
}PLAYER;

void player_ini(PLAYER *p);

void player_control(PLAYER *p);

void player_attack(PLAYER *p,WAPON w);

void player_draw(PLAYER *p,WAPON w,GUN *gun);

#endif

コード:

//wapon.h

#ifndef DEF_WAPON_H
#define DEF_WAPON_H

typedef struct{
             int count;
.
.
.
}WAPON;

typedef struct{
	int img[8];
	int img_number;
	int bullet_img;
.
.
.
}GUN;

void wapon_ini(WAPON *w);

void gun_ini(GUN *gun);

void wapon_move(WAPON *w,GUN *gun,PLAYER *p);

#endif

これらをコンパイルすると以下のエラーがでました。

wapon.h(58) : error C2061: 構文エラー : 識別子 'PLAYER'
main.cpp(43) : error C2660: 'wapon_move' : 関数に 4 個の引数を指定できません。
1>player.cpp
wapon.h(58) : error C2061: 構文エラー : 識別子 'PLAYER'
1>wapon.cpp
wapon.h(58) : error C2061: 構文エラー : 識別子 'PLAYER'

そこでwapon.hにplayer.hをインクルードすると

コード:

//wapon.h


#ifndef DEF_WAPON_H
#define DEF_WAPON_H

#include "player.h" ☆ここに追加した

typedef struct{
.
.
.
}WAPON;

typedef struct{
	int img[8];
	int img_number;
	int bullet_img;
.
.
.
}GUN;

void wapon_ini(WAPON *w);

void gun_ini(GUN *gun);

void wapon_move(WAPON *w,GUN *gun,PLAYER *p);

#endif
1>main.cpp
wapon.h(60) : error C2061: 構文エラー : 識別子 'PLAYER'
main.cpp(43) : error C2660: 'wapon_move' : 関数に 4 個の引数を指定できません。
1>player.cpp
wapon.h(60) : error C2061: 構文エラー : 識別子 'PLAYER'
1>wapon.cpp
player.h(24) : error C2061: 構文エラー : 識別子 'WAPON'
player.h(26) : error C2061: 構文エラー : 識別子 'WAPON'

さらに悪化してしまいました……
一体何が起こっているんでしょう?
下記の質問でbeatleさんが教えてくださった
どっちを先に#includeするかを決められない状態になっているんでしょうか?

分割コンパイルについて
http://dixq.net/forum/viewtopic.php?f=3&t=11496

YuO
記事: 941
登録日時: 9年前
住所: 東京都世田谷区

Re: ヘッダファイルでのインクルードについて

#2

投稿記事 by YuO » 6年前

関数の宣言に構造体の定義は不要です。
また,C++であれば無名構造体をtypedefする必要が乏しいです。
タグ名を付けた構造体は,ヘッダファイルの依存性を減らすのに役立ちます。

例えば,player.hは

コード:

//player.h
 
#ifndef DEF_PLAYER_H
#define DEF_PLAYER_H
 
struct PLAYER { // typedef名ではなく,構造体のタグ名にする
    int py,px;
    int oldy,oldx;
    bool walkflag;
.
.
.
};

struct WAPON; // 型の宣言だけをしておく
struct GUN; // 型の宣言だけをしておく

void player_ini(PLAYER *p);
 
void player_control(PLAYER *p);
 
void player_attack(PLAYER *p,WAPON w); // WAPONの宣言があるので,関数宣言は有効
 
void player_draw(PLAYER *p,WAPON w,GUN *gun); // WAPONとGUNの宣言があるので,関数宣言は有効
 
#endif
のようにします。同じように,wapon.hは,

コード:

//wapon.h
 
#ifndef DEF_WAPON_H
#define DEF_WAPON_H
 
struct WAPON{
             int count;
.
.
.
};
 
struct GUN{
    int img[8];
    int img_number;
    int bullet_img;
.
.
.
};

struct PLAYER; // 型の宣言だけしておく

void wapon_ini(WAPON *w);
 
void gun_ini(GUN *gun);
 
void wapon_move(WAPON *w,GUN *gun,PLAYER *p); // PLAYERの宣言があるので,関数宣言は有効
 
#endif
とします。これで,player.hとwapon.hの間の依存関係がなくなります。
なお,codeタグの言語指定が間違いで,Cだというのであっても,ほぼ同じ書き方ができます。
player.hで

コード:

typedef struct PLAYER PLAYER;
とかやっておけば,player.hをインクルードするファイルには影響が出ません。
関数の宣言で,宣言だけした構造体にはstructを付けなければいけないくらいでしょうか。

box
記事: 1739
登録日時: 9年前

Re: ヘッダファイルでのインクルードについて

#3

投稿記事 by box » 6年前

ヘッダーファイルを提示するのであれば、
それらをインクルードしているcppファイルも
同時に提示してくださると、
よけいなやりとりをしなくてすみます。
バグのないプログラムはない。
プログラムは思ったとおりには動かない。書いたとおりに動く。

イマダニ
記事: 145
登録日時: 7年前

Re: ヘッダファイルでのインクルードについて

#4

投稿記事 by イマダニ » 6年前

>>YuOさん
解答ありがとうございます!
YUOさんの方法でうまく行きました。

コード:

#ifndef DEF_PLAYER_H
#define DEF_PLAYER_H

#include "mouse.h"

enum Playerstate_t{
	PLAYER_STATE_DEFAULT, //通常
	PLAYER_STATE_ATTACK,  //攻撃
	PLAYER_STATE_JUMP,    //ジャンプ
	PLAYER_STATE_DAMAGE,  //ダメージ
	PLAYER_STATE_DIE,
};

struct PLAYER{
	Playerstate_t PlayerState;
	int y,x;
	int oldy,oldx;
	bool walkflag;
	bool attackflag;
	int muki;
	int img[56];
	int wapon_img[16];
	int img_number1,img_number2;
	int cnt;
	int viewx,viewy;
	int cameray,camerax;
};

struct GUN; // 型の宣言だけをしておく

void player_ini(PLAYER *p);

void player_control(PLAYER *p);

void player_attack(MOUSE m,PLAYER *p);

void player_draw(PLAYER *p,GUN *gun);



#endif

コード:

#ifndef DEF_WAPON_H
#define DEF_WAPON_H

struct GUN{
	int img[8];
	int img_number;
	int bullet_img;
	int effect_img;
	int effect_count;
	int effect_x,effect_y,sound,s_count;
	bool effectflag;
};

struct PLAYER;

void gun_ini(GUN *gun);

void wapon_move(GUN *gun,PLAYER *p);

#endif
typedefを消し、struct WAPONと型の宣言だけをするというひとつめの方法でいけました。
ですが、あまり理解できてません。どうしていけたのかよくわかっておりません。
こう書けばとりあえず動くのか、程度の理解しかしてないです。すみません……

>>関数の宣言に構造体の定義は不要です。
これは、関数の宣言、ヘッダに描いてある

void player_attack(MOUSE m,PLAYER *p);

にPLAYER p;と書かれているから
PLAYER構造体にはtypedefは必要ないということでいいんでしょうか?
そうだとして、なぜtypedefが必要が無いんでしょうか?
そして

>>これで,player.hとwapon.hの間の依存関係がなくなります。
typedefをなくし、struct WAPON; と型の宣言するだけでどうして依存関係が消えたのでしょうか?
そもそも今回の問題の原因はなんだったんでしょうか?

YuOさんの回答をみて疑問を感じたのは以上です。

>>codeタグの言語指定が間違いで
C言語です。すいません。

>>boxさん

以下が主に関連してるcppです。

コード:

#include <DxLib.h>
#include "mouse.h"
#include "player.h"
#include "key.h"
#include "wapon.h"


//プレイヤー初期化
void player_ini(PLAYER *p)
{
	//PLAYER構造体を全て0に初期化
	memset(p,0,sizeof(PLAYER));
	//プレイヤーの状態遷移を通常の状態に
	p->PlayerState = PLAYER_STATE_DEFAULT;
	//画像の読み込み
	LoadDivGraph("img/player.png",56,7,8,128,128,p->img); /*プレイヤーが立ってる画像と走る画像*/
	LoadDivGraph("img/buki.png",16,8,2,128,128,p->wapon_img);/*プレイヤーが武器を持つしぐさをしている画像*/
	//プレイヤー座標初期化
	p->x = 200;
	p->y = 300;
}



/* プレイヤー動作処理 */
void player_control(PLAYER *p)
{
	//アニメーションのカウンタ増やす
	p->cnt++;

	//プレイヤーの座標を記録
	p->oldx = p->x;
	p->oldy = p->y;

	//移動処理
	if(p->PlayerState == PLAYER_STATE_DEFAULT){/*プレイヤーが通常時*/
		/*キーが押されたら、走る方向を指定、走るフラグを立てる*/
		if(CheckKey(KEY_INPUT_A)>0 && CheckKey(KEY_INPUT_W)>0 || CheckKey(KEY_INPUT_NUMPAD7)>0){
			p->muki = 5;
			p->walkflag = true;
		} else if(CheckKey(KEY_INPUT_A)>0 && CheckKey(KEY_INPUT_S)>0 || CheckKey(KEY_INPUT_NUMPAD1)>0){
			p->muki = 0;
			p->walkflag = true;
		} else if(CheckKey(KEY_INPUT_D)>0 && CheckKey(KEY_INPUT_W)>0 || CheckKey(KEY_INPUT_NUMPAD9)>0){
			p->muki = 7;
			p->walkflag = true;
		} else if(CheckKey(KEY_INPUT_D)>0 && CheckKey(KEY_INPUT_S)>0 || CheckKey(KEY_INPUT_NUMPAD3)>0){
			p->muki = 2;
			p->walkflag = true;
		} else if(CheckKey(KEY_INPUT_A)>0 || CheckKey(KEY_INPUT_NUMPAD4)>0){
			p->muki = 3;
			p->walkflag = true;
		} else if(CheckKey(KEY_INPUT_D)>0 || CheckKey(KEY_INPUT_NUMPAD6)>0){
			p->muki = 4;
			p->walkflag = true;
		} else if(CheckKey(KEY_INPUT_W)>0 || CheckKey(KEY_INPUT_NUMPAD8)>0){
			p->muki = 6;
			p->walkflag = true;
		} else if(CheckKey(KEY_INPUT_S)>0 || CheckKey(KEY_INPUT_NUMPAD2)>0){
			p->muki = 1;
			p->walkflag = true;
		} else {
			p->walkflag = false;
		}
	}


	//向きごとの画像の指定と走る速度の指定
	if(p->PlayerState == PLAYER_STATE_DEFAULT && p->walkflag == true){
		switch(p->muki){
			case 0:
				p->y+=12;//座標移動
				p->img_number1 = 1+(p->cnt/5)%6;//アニメーション
				break;

			case 1:
				p->x+=12;
				p->y+=12;
				p->img_number1 = 15+(p->cnt/5)%6;
				break;

			case 2:
				p->x+=12;
				p->img_number1= 8+(p->cnt/5)%6;
				break;

			case 3:
				p->x-=8;
				p->y+=8;
				p->img_number1 = 36+(p->cnt/5)%6;
				break;

			case 4:
				p->x+=8;
				p->y-=8;
				p->img_number1 = 29+(p->cnt/5)%6;
				break;

			case 5:
				p->x-=12;
				p->img_number1 = 50+(p->cnt/5)%6;
				break;

			case 6:
				p->x-=12;
				p->y-=12;
				p->img_number1 = 22+(p->cnt/5)%6;
				break;

			case 7:
				p->y-=12;
				p->img_number1 = 43+(p->cnt/5)%6;
				break;
		}
	}

	//フラグが建ってない時は立ってる画像を入れる
	if(p->walkflag==false){
		if(p->muki==0) p->img_number1 = 0;
		if(p->muki==1) p->img_number1 = 14;
		if(p->muki==2) p->img_number1 = 7;
		if(p->muki==3) p->img_number1 = 35;
		if(p->muki==4) p->img_number1 = 28;
		if(p->muki==5) p->img_number1 = 49;
		if(p->muki==6) p->img_number1 = 21;
		if(p->muki==7) p->img_number1 = 42;
	}
}

//攻撃処理
void player_attack(MOUSE m,PLAYER *p){
	if(m.Click & MOUSE_INPUT_LEFT){//左クリックが押されていたら
		p->PlayerState = PLAYER_STATE_ATTACK;//プレイヤーを攻撃状態へ
		DrawString( 0, 20, "左ボタンが押されています", GetColor(255,255,255) );
	}else{
		p->PlayerState = PLAYER_STATE_DEFAULT;//それ以外は通常状態に
	}
}



/* プレイヤー描画 */
void player_draw(PLAYER *p,GUN *gun)
{
	//プレイヤの状態に合わせて描画
	switch(p->PlayerState){

	case PLAYER_STATE_DEFAULT://通常時

		//プレイヤーの立ってる画像と走ってる画像を描画
		DrawGraph(p->viewx, p->viewy, p->img[p->img_number1],TRUE);
		
		break;

	case PLAYER_STATE_ATTACK://攻撃時

		//プレイヤーが武器を使うしぐさを描画
		DrawGraph(p->viewx, p->viewy, p->wapon_img[p->img_number2],TRUE);
		//つかわれる銃を上に描画
		DrawGraph(p->viewx, p->viewy, gun->img[gun->img_number],TRUE);
			
			if(gun->effectflag==true){
			
				DrawRotaGraph(p->viewx+gun->effect_x, p->viewy+gun->effect_y, 1.5, 0.0, gun->effect_img,TRUE);
			
			}
			
			if( gun->s_count % 5 == 0 ){ // 2秒に一度
			
				PlaySoundMem( gun->sound, DX_PLAYTYPE_BACK );
			
			}
			break;
	}
}

コード:

#include <DxLib.h>
#include <string.h>
#include "mouse.h"
#include "key.h"
#include "wapon.h"
#include "player.h"

//銃初期化
void gun_ini(GUN *gun){
	memset(gun,0,sizeof(GUN));
	gun->sound=LoadSoundMem("csv/machingun.wav");
	ChangeVolumeSoundMem( 255 * 5 / 100, gun->sound ) ;
	LoadDivGraph("img/gun.png",8,8,1,128,128,gun->img);
	gun->effect_img = LoadGraph("img/発砲エフェクト.png");
}


void wapon_move(GUN *gun,PLAYER *p){

	if(p->PlayerState == PLAYER_STATE_ATTACK){
			gun->effect_count++;
			gun->s_count++;

			if(gun->effect_count==1)gun->effectflag=true;
			if(gun->effect_count==4)gun->effectflag=false;
			if(gun->effect_count==7){
				gun->effect_count=0;
			}
			
			switch(p->muki){
			case 0://左斜め前
				p->img_number2 = 0;
				gun->img_number=0;
				gun->effect_x=5;gun->effect_y=85;
				break;
			case 1://正面
				p->img_number2 = 2;
				gun->img_number=2;
				gun->effect_x=53;gun->effect_y=110;
				break;
			case 2://右斜め前
				p->img_number2 = 1;
				gun->img_number=1;
				gun->effect_x=130;gun->effect_y=90;
				break;
			case 3://左横
				p->img_number2 = 5;
				gun->img_number=5;
				gun->effect_x=-10;gun->effect_y=65;
				break;
			case 4://右横
				p->img_number2 = 4;
				gun->img_number=4;
				gun->effect_x=125;gun->effect_y=65;
				break;
			case 5://左斜め上
				p->img_number2 = 7;
				gun->img_number=7;
				gun->effect_x=-5;gun->effect_y=25;
				break;
			case 6://後ろ
				p->img_number2 = 3;
				gun->img_number=3;
				gun->effect_x=68;gun->effect_y=0;
				break;
			case 7://右斜め上
				p->img_number2 = 6;
				gun->img_number=6;
				gun->effect_x=125;gun->effect_y=25;
				break;
			}
	}
}

YuO
記事: 941
登録日時: 9年前
住所: 東京都世田谷区

Re: ヘッダファイルでのインクルードについて

#5

投稿記事 by YuO » 6年前

イマダニ さんが書きました:>>関数の宣言に構造体の定義は不要です。
これは、関数の宣言、ヘッダに描いてある

void player_attack(MOUSE m,PLAYER *p);

にPLAYER p;と書かれているから
PLAYER構造体にはtypedefは必要ないということでいいんでしょうか?
そうだとして、なぜtypedefが必要が無いんでしょうか?
typedefが必要ないのではなく,構造体の定義が不要である,ということです。
構造体の定義は,一般的に

コード:

struct tagname { members }
という形をとります。
# 手元に仕様書がない状況なので,仕様書での表現を使っていません。
構造体の宣言や定義に,typedefは関係しません。
タグ名を書くのは宣言と定義で同一の構造体であることを示すためです。
また,typedefを書かないのはtypedefでの再定義を避けるためです。
イマダニ さんが書きました:>>これで,player.hとwapon.hの間の依存関係がなくなります。
typedefをなくし、struct WAPON; と型の宣言するだけでどうして依存関係が消えたのでしょうか?
そもそも今回の問題の原因はなんだったんでしょうか?
根本的には,2つのヘッダファイルが双方をインクルードするような形になっていたことです。
そして,player.hがwapon.hを必要としていた理由はwapon.hで定義されているWAPONとGUNの2つの無名構造体を利用していることです。
しかし,この2つの無名構造体を利用しているのは関数宣言のみなので,本来宣言さえあればよく,定義が不要でした。
なので,まず構造体にタグ名を付けて識別可能にした後,player.hでは宣言だけを使う形にしています。
# typedef名は構造体自体の名前ではない。
このあたりは,Cの文法書を見てもらえれば,構造体の説明で書かれていると思われます。
イマダニ さんが書きました:>>codeタグの言語指定が間違いで
C言語です。すいません。
であれば,player.hは以下のようになります。

コード:

//player.h
 
#ifndef DEF_PLAYER_H
#define DEF_PLAYER_H
 
typedef struct PLAYER { // 構造体にタグ名を付与する
    int py,px;
    int oldy,oldx;
    bool walkflag;
.
.
.
} PLAYER; // 記述の簡略化のために,typedef名を用意しておく。
 
struct WAPON; // 型の宣言だけをしておく
struct GUN; // 型の宣言だけをしておく
 
void player_ini(PLAYER *p);
 
void player_control(PLAYER *p);
 
void player_attack(PLAYER *p,struct WAPON w); // WAPONの宣言があるので,関数宣言は有効
 
void player_draw(PLAYER *p,struct WAPON wstruct ,GUN *gun); // WAPONとGUNの宣言があるので,関数宣言は有効
 
#endif
イマダニ さんが書きました:以下が主に関連してるcppです。
Cなのでしょうか。それとも,C++なのでしょうか。
.cppは通常C++のソースファイルに使われる拡張子/サフィックスですが。

イマダニ
記事: 145
登録日時: 7年前

Re: ヘッダファイルでのインクルードについて

#6

投稿記事 by イマダニ » 6年前

コンパイラはvisualC++を使っていて、それのcppファイルにCのコードを書き込んでいます。
特に問題が無かったので、今までcppファイルに書いてましたが、これってまずいんでしょうか?

アバター
へにっくす
記事: 628
登録日時: 7年前
住所: 東京都

Re: ヘッダファイルでのインクルードについて

#7

投稿記事 by へにっくす » 6年前

イマダニ さんが書きました:コンパイラはvisualC++を使っていて、それのcppファイルにCのコードを書き込んでいます。
特に問題が無かったので、今までcppファイルに書いてましたが、これってまずいんでしょうか?
まずくはないですが、Visual C++とあるように拡張子が.cppですと、C++言語として通常コンパイルします。
C言語としてコンパイルしたいなら拡張子を.cにしてください。
あるいはコンパイルオプションですかね、、
written by へにっくす

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

Re: ヘッダファイルでのインクルードについて

#8

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

DXライブラリはC言語の文法の範囲で使うとしてもC++としてコンパイルする必要があります。
それを自覚しているか、自覚していないかは技術のレベルアップのためには重要だと思います。
C++でコンパイルしているため、C++の文法ルールが適用されることになりますのでC言語の文法からはみ出したことも可能になります。
※ DxLib.hは.cにするとコンパイルエラーになります。

【補足】 Dixq(管理人)さんは、ゲームプログラミングの館と龍神録でほぼC言語の範囲で書こうとされているはずです。
なので、C++だからと皆さんが言い出したのでイマダニさんが混乱しているのでは?

【さらに補足】 と言うことでイマダニさんは、基本的にcppの拡張子のファイルじゃないとDXライブラリを使えないので、そういう物と覚えておいてください。
みなさん、あまりその点でイマダニさんを追求しないであげてくださいね。

あと、typedef struct xxxx;せずにstruct xxxx;でxxxx 変数名;が使えるのはC++の文法の範囲です。
一応C++コンパイルしているのでコンパイルが通りますが自覚して使ってくださいね。
by softya(ソフト屋) 方針:私は仕組み・考え方を理解して欲しいので直接的なコードを回答することはまれですので、すぐコードがほしい方はその旨をご明記下さい。私以外の方と交代したいと思います(代わりの方がいる保証は出来かねます)。

イマダニ
記事: 145
登録日時: 7年前

Re: ヘッダファイルでのインクルードについて

#9

投稿記事 by イマダニ » 6年前

>>DXライブラリはC言語の文法の範囲で使うとしてもC++としてコンパイルする必要があります。※ DxLib.hは.cにするとコンパイルエラーになります。

DXライブラリがC++でないと動かないなんて初めて知りました……
これからはそれを理解したうえで、

>>まずくはないですが、Visual C++とあるように拡張子が.cppですと、C++言語として通常コンパイルします。

>>あと、typedef struct xxxx;せずにstruct xxxx;でxxxx 変数名;が使えるのはC++の文法の範囲です。
一応C++コンパイルしているのでコンパイルが通りますが自覚して使ってくださいね。

これらを自覚し、利用していこうと思います。

へにっくすさん、ソフト屋さん、解答ありがとうございました。

でもやはりC++は便利なうえ、利用している方が多いですね。
話を聞くたびにそう思います。

クラスら辺で挫折しましたけど、ロベールでまたC++の勉強を再開してみます。

今回はありがとうございました。

閉鎖

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