動的確保でのエラーについて・・・

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

動的確保でのエラーについて・・・

#1

投稿記事 by SDD » 9年前

以前のトピックでは大変お世話になりました、SDDという者です。
最近は少し余裕ができたので、プログラミングの勉強を改めて始めました。

以前に、構造化のことでかなりつまずいてしまい、正しく、きれいなコードを書くにも、それの利便性やありがたみを感じるにも、まず自分にはやはり基盤の実力と経験がまったく足りてないと感じ、
そちらの方を優先することにしました。せっかく時間をかけて教わったことを活かしきれず、申し訳ないです。

今回は要素の動的確保のことで、伺いたいことがあります。


勉強のため、龍神録を参考にしたSTGを作っており、今は自機が死んだときの、リスポーンする際のエフェクトを作りたく、動的確保での実装を試しています。一度に出る数が5個なので、
普通に配列でやった方が素直かもしれませんが、動的確保の練習がしたく、このようにしてます。

コード:


#define RES_EFF_NUM 5 //リスポーン(復活)したときのエフェクトの最大数




ch_eff_t *effp[RES_EFF_NUM];//ポインタ宣言





//自機復活のエフェクト
void enter_ch_res_eff(){
	if(ch.res_flag==1){//リスポーンしたなら
		for(int i=0;i<RES_EFF_NUM;i++){
			effp[i]=make_ch_res();//生成
		}
		for(int i=0;i<RES_EFF_NUM;i++){
			if(effp[i]!=NULL){//p[i]がNULLでなければ初期化関数呼び出し
				init_ch_res(effp[i]);//初期化
			}
		}
	}

	ch.res_flag=-1;//出したら、フラグを消す。ループする際に、生成し続けないように
}



void ch_eff_main(){
	enter_ch_res_eff();//生成や、初期化など

	for(int i=0;i<RES_EFF_NUM;i++){
		calc_ch_res(effp[i]);//動きなど計算
	}
}



//描画
void ch_eff_draw(){
	for(int i=0;i<RES_EFF_NUM;i++){
		draw_ch_res(effp[i]);
	}
}

コード:

//自機エフェクト
struct ch_eff_t{
	int cnt,img;
	double x,y,ang,spd;
};




//生成
ch_eff_t *make_ch_res(){
	ch_eff_t *p;
	p=(ch_eff_t *)malloc(sizeof(ch_eff_t));
	if(p==NULL){
		printfDx("リスポーンエフェクトのメモリ領域の取得に失敗しました。");
		exit(0);//強制終了
	}
	return p;
}



//初期化関数
void init_ch_res(ch_eff_t *p){
	p->x=GetRand(100)+150;
	p->y=GetRand(400)+50;
	p->img=img_etc[0];//とりあえず、よそで取得したハンドルを使いまわし
	p->cnt=0;
	p->spd=GetRand(3)+1.0;
}





//使用したリスポーン用のメモリ領域を開放
ch_eff_t *delete_ch_res_eff(ch_eff_t *p){
	if(p!=NULL){//pがヌルでなければ
		free(p);
		p=NULL;
		return p;
	}
	return NULL;
}



//エフェクト計算
void calc_ch_res(ch_eff_t *p){
	if(p!=NULL){//pがNULLでなければ
		p->ang=atan2(ch.y-p->y,ch.x-p->x);

		p->x+=p->spd*cos(p->ang);
		p->y+=p->spd*sin(p->ang);

		p->cnt++;
		if(p->cnt>=60){//カウントが1秒以上なら
			p=delete_ch_res_eff(p);
		}
	}
}






//エフェクト描画
void draw_ch_res(ch_eff_t *p){
	if(p!=NULL){
		DrawRotaGraphFdF(p->x,p->y,0.5,p->ang+PI/2,p->img,TRUE);
	}
}

現状では、自機が死ぬと、外部のch.res_flagが立ち、それによって復活のエフェクトが出てくるという感じで、問題は一度目の死亡では無事エフェクトが生成され、描画もされるのですが、
二度目の死亡のときに、p=(ch_eff_t *)malloc(sizeof(ch_eff_t));の部分を指して、

「Windows によって 実験.exe でブレークポイントが発生しました。

ヒープが壊れていることが原因として考えられます。実験.exe または読み込まれた DLL にバグがあります。

あるいは、実験.exe がフォーカスを持っているときに、ユーザーが F12 キーを押したことが原因として考えられます。

可能であれば、出力ウィンドウに詳細な診断情報が表示されます。」

というエラーで出て、プログラムが終了してしまいます。


生成部や、破棄部をブレークポイントで追ってみたり考えたりしたのですが、どうしても原因がわかりません。

見難いコードですみませんが、問題のある部分など、アドバイスをいただければと思い、質問いたします。


環境はWin7、VC++2008EE、DXライブラリを使用しています。

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

Re: 動的確保でのエラーについて・・・

#2

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

それは、mallocの管理情報部が壊れた時にでるエラーです。つまり、ポインタの指す先とか配列添字外の書いてはいけない所に書いた結果、mallocの管理情報が破損(ヒープが壊れている)したため継続不能ですってエラーを出します。
by softya(ソフト屋) 方針:私は仕組み・考え方を理解して欲しいので直接的なコードを回答することはまれですので、すぐコードがほしい方はその旨をご明記下さい。私以外の方と交代したいと思います(代わりの方がいる保証は出来かねます)。

アバター
h2so5
副管理人
記事: 2212
登録日時: 13年前
住所: 東京
連絡を取る:

Re: 動的確保でのエラーについて・・・

#3

投稿記事 by h2so5 » 9年前

最大の問題は引数が参照渡しされていると誤解していることですね。
pに代入している箇所がいくつかありますが、pは値渡しされているので呼び出し元の変数を書き換えることはできません。

SDD

Re: 動的確保でのエラーについて・・・

#4

投稿記事 by SDD » 9年前

ソフト屋さん、h2so5さん
アドバイスありがとうございます。

更新系の関数の引数にポインタのポインタを指定するということでしょうか。
実は、書き換えができてないようなのはデバッグですこし気付いていたのですが(じゃあやれって感じですが)、いまいち確信が
もてず、質問しました。

ソフト屋さんの仰ることの原因がそれなのかはまだ分かりませんが、とりあえず、pのアドレスを渡すようにしてみます。

アバター
h2so5
副管理人
記事: 2212
登録日時: 13年前
住所: 東京
連絡を取る:

Re: 動的確保でのエラーについて・・・

#5

投稿記事 by h2so5 » 9年前

そもそも関数内でpを書き換える必要があるのでしょうか。

SDD

Re: 動的確保でのエラーについて・・・

#6

投稿記事 by SDD » 9年前

h2so5さん
すみません、どういうことかよくわかりません。
必要ないことをしてしまっているのでしょうか。

アバター
h2so5
副管理人
記事: 2212
登録日時: 13年前
住所: 東京
連絡を取る:

Re: 動的確保でのエラーについて・・・

#7

投稿記事 by h2so5 » 9年前

そのままの意味です。
・calc_ch_resからはpを書き換えられない
・そもそもcalc_ch_resはエフェクトの軌道計算のための関数なのでエフェクトの解放をするのはおかしい
・エフェクトの寿命が尽きているかどうかはcalc_ch_resの中でなくてもわかる

ISLe()

Re: 動的確保でのエラーについて・・・

#8

投稿記事 by ISLe() » 9年前

構造化できないうちに動的確保やると余計にハマると思いますよ。

SDD

Re: 動的確保でのエラーについて・・・

#9

投稿記事 by SDD » 9年前

ISLeさん
以前はお世話になりました。
本当ですか・・・。
ただ、習得する順番が逆かもしれませんが、あのトピックも途中で投げて、これもやっぱり投げる、ということはマナー的にも個人的にも
避けたいので、今回は途中で投げないよう、解決するよう努力します。


h2so5さん
なるほど・・・。
下二つに関しては、僕もよく理解できたのですが、一つ目のことで、
さっきの自分の発言とは矛盾するのですが、プログラムを実行し、calc_ch_resを実行した結果、エフェクトが自機に向かって動いてくるのを確認
できたのですが、これは書き換えられているということではないのでしょうか・・・。デバッグしてみて、その関数を抜けたあとeffpを調べると
しっかりcalc_ch_resの通りに変わっているのですが・・・。

アバター
h2so5
副管理人
記事: 2212
登録日時: 13年前
住所: 東京
連絡を取る:

Re: 動的確保でのエラーについて・・・

#10

投稿記事 by h2so5 » 9年前

SDD さんが書きました: 下二つに関しては、僕もよく理解できたのですが、一つ目のことで、
さっきの自分の発言とは矛盾するのですが、プログラムを実行し、calc_ch_resを実行した結果、エフェクトが自機に向かって動いてくるのを確認
できたのですが、これは書き換えられているということではないのでしょうか・・・。デバッグしてみて、その関数を抜けたあとeffpを調べると
しっかりcalc_ch_resの通りに変わっているのですが・・・。
p->x, p->y を書き換えることはもちろんできますよ。pはコピーですがx, yは間接参照ですから。
しかし p = NULL と代入しても effp の中のどれかが NULL に書き換わることはありません。

SDD

Re: 動的確保でのエラーについて・・・

#11

投稿記事 by SDD » 9年前

道理で、座標などは関数を抜けても変更されているのに、p=NULLなどだけは関数を抜けると
なかったことになってるわけですね。

現状では、pの参照先は変更できるが、pそのものを変えることはできないということですね。

SDD

Re: 動的確保でのエラーについて・・・

#12

投稿記事 by SDD » 9年前

連投すみません。

以下のように、freeする関数の引数に、effpのアドレスをとる関数を作り、訂正しました。
不恰好かもしれませんが・・・

コード:

void ch_eff_main(){
	enter_ch_res_eff();//生成や、初期化など

	for(int i=0;i<RES_EFF_NUM;i++){
		calc_ch_res(effp[i]);//動きなど計算
	}
	for(int i=0;i<RES_EFF_NUM;i++){
		delete_ch_res_eff(&effp[i]);
	}
}

コード:

//使用したリスポーン用のメモリ領域を開放
void delete_ch_res_eff(ch_eff_t **p){
	if(*p!=NULL){//pがヌルでなければ
		if((*p)->cnt>=60){//出現してから一秒たったら
			free(*p);
			*p=NULL;
		}
	}
}
しばらく実行し、いろいろやったり、ブレークポイントを使って追ってみたりしましたが、現状では異常はおきませんでした。

とりあえず、提示し、解決はまた後にします。

ISLe()

Re: 動的確保でのエラーについて・・・

#13

投稿記事 by ISLe() » 9年前

与えられたもの(ch_eff_t)を抽象化せずそのまま使っているだけでは、どんどん構造化から離れていきますよ。
コーディングスタイルだけでなく精神(意識・考え方)的に。
そうなってしまうと最悪取り返しが付かなくなります。

No.10で、間接参照したメンバは書き換えできるという重大なヒントがあったのですが。

SDD

Re: 動的確保でのエラーについて・・・

#14

投稿記事 by SDD » 9年前

ISLeさん
そうですね・・・。自機系のエフェクトの管理部も作りたいし、(前回投げ出したため、未だに管理部がよくわかっていない)
今自分が汎用性の低いコードを書いているということはよくわかります。
構造化に対して苦手意識ができてしまったので、克服したいと思います。

いきなり質問で申し訳ないのですが、引数にch_eff_tを取ることがすでに問題なのでしょうか。それとも
内部で、主に
if((*p)->cnt>=60)
のような文などを書いていることが具体的すぎるということでしょうか。
よくよく考えたら、これは前回のトピックでの、元々のenemy_enterと同じになってしまってるっていうことですかね・・・。

SDD

Re: 動的確保でのエラーについて・・・

#15

投稿記事 by SDD » 9年前

すみません、このトピックの本筋からはずれるような質問をしてしまいました。
今回の動的確保でのアクセス違反は、メモリ領域を確保したあと、関数内で参照できていないpをNULLにしたと
勘違いし、解放後にも関数でアクセスしていたためのものだと思われます。これでこのトピックは解決にしたいと思います。
h2so5さん、ソフト屋さん、ISLeさん、今回もお世話になりました。

閉鎖

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