分割コンパイルでおこるエラーについて

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

分割コンパイルでおこるエラーについて

#1

投稿記事 by becky » 14年前

C言語を学校で勉強した知識で、c++でゲームプログラミングを行なっています
(c++について知らないことも多いのでこの時点であまり良くないことをしているわけなんですが・・・クラスについても勉強していません)

RPGの戦闘システムを作り全てをメイン関数に収めて作った段階では動作するようになったので、
プログラミングの館の分割コンパイルを参考にしながら分割コンパイルをしようと思ったのですが、このやり方がうまくいきません。
~~~は既にmain.objで定義されています
とエラーが出るのです。

ネットで調べ、extern をつければよいとわかったので該当するようなところにつけてみたりしたのですが、今度は
外部シンボル ""~~~" は未解決です。
というエラーが出て、さらに幾つかの「既にmain.objで定義されています」も消えずに残ったままです。

一体どうすればいいかがわかりません。

長いプログラムを作ってから分割しようとしたため1度にでるエラーも多く、解決がしづらい状況になってしまったのは申し訳ないです。
下に、主な変数が書いてあるini.hを書きます。このini.hは分割したmain.cppとbattle.cpp両方でインクルードしています。

コード:

#ifndef INI_H //二重include防止
#define INI_H

extern int Key[256]; // キーが押されているフレーム数を格納する
extern int font;//フォントを格納する変数
extern int img[100];//画像データ
extern int img_card[100];//カード画像データ
extern int img_enemy[100];//敵画像データ
extern int White;//白の格納
extern int count; //時間
extern int enemycount; //敵の数
extern int currentenemycount; //現在の敵の数
extern int cardorder; //次に引くカードが何枚目か
// キーの入力状態を更新する
extern int gpUpdateKey(){
        char tmpKey[256]; // 現在のキーの入力状態を格納する
        GetHitKeyStateAll( tmpKey ); // 全てのキーの入力状態を得る
        for( int i=0; i<256; i++ ){ 
                if( tmpKey[i] != 0 ){ // i番のキーコードに対応するキーが押されていたら
                        Key[i]++;     // 加算
                } else {              // 押されていなければ
                        Key[i] = 0;   // 0にする
                }
        }
        return 0;
}

//選択肢用構造体
typedef struct{
	int x,y;
	char name[100];
} Menu;

//キャラ用構造体
typedef struct{
	char name[100]; //モンスターの名前
	int own; //キャラの種類の判定
	int hp;  //体力
	int hp_max;  //最大体力
	int atk;  //攻撃力
	int def;  //防御力
	int spi;  //スピード
	int order; //攻撃順番
	int flaglive; //生存判定フラグ 0は死んだ直後、1は生きてる、2は死後
	int flagatk;//攻撃判定フラグ
	int pointx;//x座標
} Chara;

extern Chara chara[100] = {
	{"自分",0,300,300,300,0,100,1,1,1,0},
	{"敵1",1,400,400,100,0,80,0,1,1,0},
	{"敵2",2,200,400,50,0,60,0,1,1,0},
	{"敵3",3,100,100,30,0,40,0,1,1,0},
};

extern Chara battlechara[100]={
	{"",0,300,300,300,0,100,1,1,1,0},//自分用キャラ
	{"",1,400,400,100,0,80,0,1,1,0},//敵用キャラ1
	{"",2,200,400,50,0,60,0,1,1,0},//敵用キャラ2
	{"",3,100,100,30,0,40,0,1,1,0},//敵用キャラ3
	{"",2,500,500,20,0,20,0,1,1,0},//敵用キャラ4
};

typedef struct{
	int number; //カードが並ぶ順番決める乱数
	int type; //カードの種類
	int atk;  //カード攻撃力
	int dif;  //カード防御力
	int exist; //カードが場に存在するかの判定
	int exorder; //場に存在するカードの順番
} Card;

extern Card card[100];

#endif 
これを利用してコンパイルすると

1>main.cpp
main.cpp(70) : warning C4717: 'graph_main' : すべてのコントロールのパス、関数を回帰するとランタイム スタック オーバーフローが発生します。

>main.obj : error LNK2005: "int __cdecl gpUpdateKey(void)" (?gpUpdateKey@@YAHXZ) は既に battle.obj で定義されています。
1>main.obj : error LNK2005: "struct Chara * chara" (?chara@@3PAUChara@@A) は既に battle.obj で定義されています。
1>main.obj : error LNK2005: "struct Chara * battlechara" (?battlechara@@3PAUChara@@A) は既に battle.obj で定義されています。
1>battle.obj : error LNK2001: 外部シンボル ""int * Key" (?Key@@3PAHA)" は未解決です。
1>main.obj : error LNK2001: 外部シンボル ""int * Key" (?Key@@3PAHA)" は未解決です。
1>battle.obj : error LNK2001: 外部シンボル ""int enemycount" (?enemycount@@3HA)" は未解決です。
1>main.obj : error LNK2001: 外部シンボル ""int enemycount" (?enemycount@@3HA)" は未解決です。
1>battle.obj : error LNK2001: 外部シンボル ""struct Card * card" (?card@@3PAUCard@@A)" は未解決です。
1>main.obj : error LNK2001: 外部シンボル ""struct Card * card" (?card@@3PAUCard@@A)" は未解決です。
1>battle.obj : error LNK2001: 外部シンボル ""int cardorder" (?cardorder@@3HA)" は未解決です。
1>main.obj : error LNK2001: 外部シンボル ""int cardorder" (?cardorder@@3HA)" は未解決です。
1>battle.obj : error LNK2001: 外部シンボル ""int currentenemycount" (?currentenemycount@@3HA)" は未解決です。
1>main.obj : error LNK2001: 外部シンボル ""int currentenemycount" (?currentenemycount@@3HA)" は未解決です。
1>battle.obj : error LNK2001: 外部シンボル ""int * img" (?img@@3PAHA)" は未解決です。
1>main.obj : error LNK2019: 未解決の外部シンボル "int * img" (?img@@3PAHA) が関数 _WinMain@16 で参照されました。
1>battle.obj : error LNK2001: 外部シンボル ""int font" (?font@@3HA)" は未解決です。
(以下省略)

理解が追いついていないので長いものを貼ってしまいすいません・・・。
もし、他に記述すべき箇所があれば記述します。どうすればよいのか教えていただければありがたいです。

hidden

Re: 分割コンパイルでおこるエラーについて

#2

投稿記事 by hidden » 14年前

エラーは一番上から潰すといいですよ
ってことでgraph_mainを見せてください。

becky

Re: 分割コンパイルでおこるエラーについて

#3

投稿記事 by becky » 14年前

main.cpp部分に

コード:

//描画メイン
void graph_main()
{
	graph_battle();

}
battle.cpp部分に

コード:

void graph_battle()
{
	graph_waku();
	if(Command == 0){
		graph_top();
		graph_status();
		graph_enemy();
	}
	else if(Command == 1){
		if(BattleCom == 0){
			graph_selectcard();
			graph_enemy();
		}
		else if(BattleCom == 1){
			graph_selectmons();
		}
			graph_status();
	}
	else if(Command == 2){
		graph_result();
		graph_enemy();
	}
	else if(Command == 3){
		graph_enemy();
		graph_win_lose();
	}
}
となっています。
以下各関数の定義です(graph_battleの上に記述してあります)。また長くなってしまいます。すいません。。。

コード:

//枠描画
void graph_waku()
{
	DrawGraph(0,0,img[2],TRUE);
	DrawGraph(490,350,img[0],TRUE);
	DrawGraph(0,350,img[1],TRUE);
}
//トップ画面描画
void graph_top()
{
	
	for( int i=0; i<3; i++ ){ // メニュー項目を描画
	   DrawFormatStringToHandle( menu[i].x, menu[i].y, GetColor(255,255,255),font, menu[i].name );
	}
	//選択中の色変え
	DrawFormatStringToHandle( menu[SelectMenu].x, menu[SelectMenu].y, GetColor(255,255,0),font, menu[SelectMenu].name );
}

//ステータス描画
void graph_status()
{
	//ステータス
	DrawFormatStringToHandle( 10, 385, White,font, "俺外野手" );
	DrawFormatStringToHandle( 340, 400, White,font, "HP:%d",battlechara[0].hp);
	//ステータスバー
	DrawBox( 100 , 390 , 400 , 400 ,GetColor(255,255,0) , TRUE ) ;
	if(battlechara[0].hp != battlechara[0].hp_max){
		int bar = (battlechara[0].hp_max-battlechara[0].hp) * 300 / battlechara[0].hp_max;
		DrawBox(400-bar,390,400,400,GetColor(255,0,0),TRUE);
	}
}

//バトル描画
void graph_selectcard()
{
	//判定ついてる5つだけでまた1~5を配置しなおす
	int tmp = 0;
	for(int u=0;u<100;u++){
		if(card[u].exist == 1){
			if(card[u].exorder == SelectBattle){
				DrawGraph(20+110*tmp,190,img_card[card[u].type],TRUE);
			}
			else{
				DrawGraph(20+110*tmp,200,img_card[card[u].type],TRUE);
			}
			tmp++;
		}
	}
}

//攻撃場面描画
void graph_selectmons()
{
	for(int z=1;z<(enemycount+1);z++){
		if(battlechara[z].flaglive == 1){//生きてたら
			if(SelectMons == z && (count/30)%2 ==0){//そして選ばれてて、時間が規定を満たしてれば
			}//なんもなし
			else{
				DrawGraph(battlechara[z].pointx,20,img_enemy[battlechara[z].own],TRUE);
				DrawFormatStringToHandle(battlechara[z].pointx,40,White,font,"HP:%d",battlechara[z].hp);  //敵のHP
			}//そうでないとき描写
		}
	}
}
//結果描画
void graph_result()
{

	if(atkchara == 11){
		DrawFormatStringToHandle( 10, 375, White,font, "俺の攻撃");
		DrawFormatStringToHandle( 10, 400, White,font, "%sに%dのダメージ!",chara[battlechara[SelectMons].own].name,damage[0]);
		int tmp = 0;
	}
	if(atkchara == 12){
		DrawFormatStringToHandle( 10, 375, White,font, "俺の防御");
		DrawFormatStringToHandle( 10, 400, White,font, "未実装であまり意味が無い");
		int tmp = 0;
	}
	for(int z=1;z<(enemycount+1);z++){
		 if(atkchara == z){
			DrawFormatStringToHandle( 10, 375, White,font, "%sの攻撃",chara[battlechara[z].own].name);
			DrawFormatStringToHandle( 10, 400, White,font, "俺に%dのダメージ!",damage[1]);
		}
	}

}
//敵描画

void graph_enemy()
{
	for(int z=1;z<(enemycount+1);z++){
		if(battlechara[z].flaglive == 1){//生きてたら
			DrawGraph(battlechara[z].pointx,20,img_enemy[battlechara[z].own],TRUE);
			DrawFormatStringToHandle(battlechara[z].pointx,40,White,font,"HP:%d",battlechara[z].hp);  //敵のHP2
		}
	}
}

hidden

Re: 分割コンパイルでおこるエラーについて

#4

投稿記事 by hidden » 14年前

あ、見間違ったかもしれないです
なんかアホなこと言ったかもorz

becky

Re: 分割コンパイルでおこるエラーについて

#5

投稿記事 by becky » 14年前

コード:

お手数かけます。

main.cppに書いてあるデータロードがエラーの起こっている起因になっているかもしれないので、それも貼っておきます。


//データロード
void load(){
	font= CreateFontToHandle( "メイリオ", 15, 3, DX_FONTTYPE_ANTIALIASING_EDGE ) ;//"メイリオ"  の30pt,太さ3のフォントを作成
	White=GetColor(255,255,255);//Whiteに白を入れる
	img[0]=LoadGraph("img/waku1.png");
	img[1]=LoadGraph("img/waku2.png");
	img[2]=LoadGraph("img/tower.jpg");
	img_enemy[1]=LoadGraph("img/mon1.png");
	img_enemy[2]=LoadGraph("img/mon2.png");
	img_enemy[3]=LoadGraph("img/mon3.png");
	img_enemy[4]=LoadGraph("img/mon2.png");
	img_enemy[5]=LoadGraph("img/mon1.png");
	img_card[0]=LoadGraph("img/card0.png");
	img_card[1]=LoadGraph("img/card1.png");
	enemycount = 3; //モンスターの数
	currentenemycount = enemycount;//動かすほう
	battlechara[0] = chara[0];
	for(int z=1;z<(enemycount+1);z++){
		battlechara[z] = chara[GetRand(2)+1];
	}
	for(int u =0;u<(enemycount+1);u++){
		battlechara[u].flagatk = 1;	
		battlechara[u].flaglive = 1;	
		battlechara[u].pointx = (((u-1) * 640)/(enemycount)+ 640/((enemycount)*2)) -40;
	}
	cardorder = 4; //2ターン目6枚目にひくので
}

hidden

Re: 分割コンパイルでおこるエラーについて

#6

投稿記事 by hidden » 14年前

なんか申し訳ないです。
ini.hでexternをかけている変数群の実体はどこにあります?

becky

Re: 分割コンパイルでおこるエラーについて

#7

投稿記事 by becky » 14年前

実態という言葉は見たことがあるのですがどういうことでしょうか?

例えばini.hに

コード:

extern int img[100];//画像データ
extern int img_card[100];//カード画像データ
extern int img_enemy[100];//敵画像データ
とありますが、load関数内の

コード:

    img[0]=LoadGraph("img/waku1.png");
    img[1]=LoadGraph("img/waku2.png");
    img[2]=LoadGraph("img/tower.jpg");
    img_enemy[1]=LoadGraph("img/mon1.png");
    img_enemy[2]=LoadGraph("img/mon2.png");
    img_enemy[3]=LoadGraph("img/mon3.png");
    img_enemy[4]=LoadGraph("img/mon2.png");
    img_enemy[5]=LoadGraph("img/mon1.png");
    img_card[0]=LoadGraph("img/card0.png");
    img_card[1]=LoadGraph("img/card1.png");
この記述で実態ということにならないのでしょうか?

1>battle.obj : error LNK2001: 外部シンボル ""int * img_card" (?img_card@@3PAHA)" は未解決です。
1>main.obj : error LNK2001: 外部シンボル ""int * img_card" (?img_card@@3PAHA)" は未解決です。
1>battle.obj : error LNK2001: 外部シンボル ""int * img_enemy" (?img_enemy@@3PAHA)" は未解決です。
1>main.obj : error LNK2001: 外部シンボル ""int * img_enemy" (?img_enemy@@3PAHA)" は未解決です。

というエラーが出ているのでなっていないのでしょうが・・・。

びす

Re: 分割コンパイルでおこるエラーについて

#8

投稿記事 by びす » 14年前

externは「その変数の実体がどこかで作成されている」という宣言なのに、
どこを探しても作成されてないぞーってエラーだと思います

imgだけエラーがでていないので、どこかで以下のようにimg[100]を作成してる行があると思います

コード:

int img[100]//画像データ
そこを以下のように書き換えてみてください

コード:

int img[100];//画像データ
int img_card[100];//カード画像データ
int img_enemy[100];//敵画像データ
この記述で実態ということにならないのでしょうか?
すでにある配列にLoadGraphの戻り値を入れているだけで、実体の作成はされてません

becky

Re: 分割コンパイルでおこるエラーについて

#9

投稿記事 by becky » 14年前

imgについてのエラーも出ていました・・・。書かなくて申し訳ありません。

アドバイスを見ていじってみた(メイン関数にint img[100]等を追加した)結果、無事分割してもコンパイルが通るようになりました。協力してくださったみなさんありがとうございます。


解決済みを押す前に質問がいくつかあります。


externは、例えば3つのcppプログラムのヘッダーに
extern int a;
と書いてあり、その3つのうち最初に読み込ませたいプログラムに
int a;
と書いてあれば3つのプログラムの中で自由にaという変数を利用することができるようになるということなのでしょうか?


今、全てをC言語の知識で書いていますが(staticを使っているように)、
今後c++でプログラムを書いていきたいのであれば
大規模なプログラムになった時のためにクラスの仕組みを勉強して利用したほうがよいのですか?

個人的な感想になるかもしれませんが意見をくださるとありがたいです。

box
記事: 2002
登録日時: 15年前

Re: 分割コンパイルでおこるエラーについて

#10

投稿記事 by box » 14年前

becky さんが書きました: externは、例えば3つのcppプログラムのヘッダーに
cppプログラム、と書くよりはcppファイルと書く方が適切ではないかと思います。
becky さんが書きました: extern int a;
と書いてあり、その3つのうち最初に読み込ませたいプログラムに
プログラム、と書くよりは、やはりファイルとかソースコードとか書く方が適切ではないかと思います。
さて、
becky さんが書きました: と書いてあり、その3つのうち最初に読み込ませたいプログラムに
最初に読み込ませたい、という制約は特にありません。
どこか1箇所で、externを付けないことで実体を定義しておけばよいです。
バグのないプログラムはない。
プログラムは思ったとおりには動かない。書いたとおりに動く。

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

Re: 分割コンパイルでおこるエラーについて

#11

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

1.
プログラムのビルドは、ソースファイル→[コンパイル]→オブジェクトファイル→[リンク]→実行ファイルの段階を経て作成されます。
リンクの段階で1つの実行に統合されますので、同じ名前のグローバル変数の実体が複数のオブジェクトファイルにあると多重定義エラーとなります。あるいは1つも実体がないのにexternで参照されていれば未定義エラーとなります。グローバル変数の実体が何処のオブジェクトファイルにあるかは関係有りませんので、何処かに実体があれば良いのです。

2.
C言語でもカプセル化は可能ですし、グローバル変数を無くて変数を隠蔽したり、データ構造を隠蔽することも出来ます。データ構造と関数を密接に結びつけることも出来ます。ソースファイルを機能単位で分けることでクラスのような意味を持たせることも出来ます。つまり、C言語でもちゃんと意識してプログラム・データ構造を作ればクラス的なことは可能です。
なので、クラスで設計する以前に変数を公開する公開しないを意識することや大規模なプログラムを組むための練習は出来ると思います。
ここで質問してもらえば、良いプログラム設計か悪い設計かの回答は得られますので活用して下さい。

ちなみに今のプログラムの設計として悪い点は
・最小限以上の構造体をヘッダで公開していること。
・グローバル変数が多いこと。
・定数やenumを使わず直値を多用。
・ソースファイルが機能別に分かれていない。
・ヘッダにプログラムがある
などです。
あとexternがあるのに初期値が書いてありますが、実体の方に初期値は書いて下さい。
by softya(ソフト屋) 方針:私は仕組み・考え方を理解して欲しいので直接的なコードを回答することはまれですので、すぐコードがほしい方はその旨をご明記下さい。私以外の方と交代したいと思います(代わりの方がいる保証は出来かねます)。

becky

Re: 分割コンパイルでおこるエラーについて

#12

投稿記事 by becky » 14年前

みなさん丁寧な回答ありがとうございます。
また、何かわからないことがあれば質問させて頂きます。

閉鎖

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