ページ 1 / 1
不明なポインタへのアクセス
Posted: 2009年10月26日(月) 21:33
by ひよこ
現在クラスを勉強しようとソースを組みながら、リストを作っていたんですが、意味のわからない
ポインタエラーがでて困っています。どうすればいいでしょうか?
敵を描こうとするとポインタエラーになってしまいます。
enemyクラス
struct enemy_t{
struct enemy_t *next; /* 次の構造体変数のアドレス */
bullet_data bullet;//クラスにアクセス用
double x,y; //座標
double spd;//スピード
double angle;//角度
int flag;//フラグ
int cnt;//カウンタ
int pattern;
int knd;
int muki;
double vx,vy;
int wait;
int state;
int img;
};
class enemy_data{
private:
enemy_t enemy;
public:
void enemy_enter(int flag,double x,double y ,double spd,double angle,int pattern,int knd,int muki,int wait);
void enter_enemy(enemy_t *p,double x,double y ,double spd,double angle,int pattern,int knd,int muki,int wait);
void calc_enemy();
void graph_enemy();
void enemy_graph(enemy_t *head);
void Init();
};
クラス内容
void enemy_data::Init(){
memset(&enemy,0,sizeof(enemy_t));
//弾を初期化
enemy.bullet.Init();
//敵をセット
enemy_enter(0,50,30,0,PI/2,0,0,0,0);
enemy_enter(1,50,30,3,PI/2,0,0,0,0);
enemy_enter(1,50,30,3,PI/2,0,0,0,0);
//弾をセット
enemy.bullet.enter(0,-10,50,PI/2,0,8,0);
enemy.bullet.enter(1,100,60,PI/2,3,1,0);
}
void enemy_data::graph_enemy(){
enemy_graph(&enemy);
enemy.bullet.graph_bullet();
}
void enemy_data::enemy_graph(enemy_t *head){
enemy_t *p;
int countu=0;
if(head->next!=NULL){
for(p=head->next;p!=head;p=p->next){
if(p->flag==1){//アクセス違反が発生
p->x+=cos(p->angle)*(p->spd);
p->y+=sin(p->angle)*(p->spd);
DrawRotaGraphFdF(p->x,p->y,1.0,p->angle+PI/2,img_enemy[0][0],TRUE);
p->cnt++;
countu++;
}
}
}
}
void enemy_data::calc_enemy(){
if(CheckStatePad(configpad.shot)>=1){
enemy_enter(1,50,30,3,PI/2,0,0,0,0);
}
enemy.bullet.delete_bullet();
enemy.bullet.calc_bullet(1);
}
void enemy_data::enemy_enter(int flag,double x,double y ,double spd,double angle,int pattern,int knd,int muki,int wait){
if(flag==0)
enter_enemy(&enemy,x,y,spd,angle,pattern,knd,muki,wait);
if(flag==1)
enter_enemy(enemy.next,x,y,spd,angle,pattern,knd,muki,wait);
}
void enemy_data::enter_enemy(enemy_t *p, double x, double y, double spd, double angle, int pattern, int knd,int muki,int wait){
enemy_t *tmp;
tmp = (enemy_t*)malloc(sizeof(enemy_t));
//要素が存在した場合
if(p->next!=NULL)
{
tmp->next=p->next;
p->next=tmp;
tmp->flag=1;
tmp->x=x;
tmp->y=y;
tmp->spd=spd;
tmp->angle=angle;
tmp->cnt=0;
tmp->pattern=pattern;
tmp->knd=knd;
tmp->muki=muki;
tmp->vx=0;
tmp->vy=0;
tmp->wait=wait;
tmp->state=0;
tmp->img=0;
}
else//要素がない場合
{
tmp->next=p->next;
p->next=tmp;
tmp->flag=1;
tmp->x=x;
tmp->y=y;
tmp->spd=spd;
tmp->angle=angle;
tmp->cnt=0;
tmp->pattern=pattern;
tmp->knd=knd;
tmp->muki=muki;
tmp->vx=0;
tmp->vy=0;
tmp->wait=wait;
tmp->state=0;
tmp->img=0;
}
}
Re:不明なポインタへのアクセス
Posted: 2009年10月26日(月) 21:45
by kazuoni
どのようにこのクラスを使って、どのような時に、どこでエラーが出るのかなど、
不明な点が多すぎるので、補足説明or全コードUP
すれば、よりよい回答が得られるかと思います。
Re:不明なポインタへのアクセス
Posted: 2009年10月26日(月) 21:55
by ひよこ
enemy_data::graph_enemy();を使って描画しようとすると、
enemy_graph(&enemy);でアクセス違反が起きてしまいます。
Re:不明なポインタへのアクセス
Posted: 2009年10月26日(月) 21:56
by box
> for(p=head->next;p!=head;p=p->next){
> if(p->flag==1){//アクセス違反が発生
ザッと見ただけですけど、このループは、pがheadでない間回り続けますね。
ということは、p=p->nextでもって、pがNULLになるところまで来ると思うのです。
その状態ではメンバーflagを指せないため、アクセス違反が起きるのではないでしょうか。
Re:不明なポインタへのアクセス
Posted: 2009年10月26日(月) 22:11
by ひよこ
pがNULLにきてアクセス違反となります。その通りなのですが、
いつアクセス違反になったのか?原因は周りのソースが悪いか?描画の関数が悪いのか?
どこが原因なのかがわかりません。
Re:不明なポインタへのアクセス
Posted: 2009年10月26日(月) 22:19
by softya
えーと、enemy_dataクラスのインスタンスはいつ生成を?
それとInitはいつ呼び出しを?
色々と怪しいのですが、bullet_dataクラスを含む構造体をmemset(&enemy,0,sizeof(enemy_t));すんのはやばいと思います。
>pがNULLにきてアクセス違反となります。その通りなのですが、
>いつアクセス違反になったのか?原因は周りのソースが悪いか?描画の関数が悪いのか?
悪いのはこいつでは?
> for(p=head->next;p!=head;p=p->next){
pがNULLの時を全く考慮していません。
Re:不明なポインタへのアクセス
Posted: 2009年10月26日(月) 22:58
by ひよこ
いろいろとわかりづらくてすみません。
関数はクラスGameのなかで呼ばれてWinMainのところでインスタンス?Gameの実体を作り、
Init(すべて初期化)>>Calc>>Graphの順でよんでCalc,Graphでループします。
for(p=head->next;p!=head;p=p->next)は下のbulletクラスが同じ構造で同じように使っても、
アクセス違反にはならなかったので困っています。
Re:不明なポインタへのアクセス
Posted: 2009年10月26日(月) 23:17
by softya
>for(p=head->next;p!=head;p=p->next){
p!=headの条件を満たす事はありますか?
私の勘違いでなくれば、head=&enemyのアドレスをp->nextあるいはtmp->nextに代入している場所が見当たりませんが?
一度、デバッグのため出来上がったenemy_tのリストをダンプして確認する関数を作る事をお勧めします。
あるいは、
>for(p=head->next;p!=head;p=p->next){
のループをデバッガで追いかけてください。
Re:不明なポインタへのアクセス
Posted: 2009年10月26日(月) 23:48
by ひよこ
一度、デバッグのため出来上がったenemy_tのリストをダンプして確認する関数を作る事をお勧めします。
>>ダンプってなんですか?
ループをデバッガで追いかけてください。
>>値をみてと言っているのでしょうか?
というかループしているうちに0x000000 NULLにいっているような場所が見当たりません。
Re:不明なポインタへのアクセス
Posted: 2009年10月26日(月) 23:53
by softya
では、異常終了したときにデバッガで「呼び出し履歴」を追っかけてみてください。
どこからどのように呼び出されたか調べることができます。
Re:不明なポインタへのアクセス
Posted: 2009年10月27日(火) 00:11
by ひよこ
使い方がわかりません、教えてください。
Re:不明なポインタへのアクセス
Posted: 2009年10月27日(火) 00:48
by Poco
http://dsas.blog.klab.org/archives/51114040.html
↑の一番下の画像に「呼び出し履歴」なるペインがあります。
そこを見てください、っていう意味です。
Re:不明なポインタへのアクセス
Posted: 2009年10月27日(火) 00:57
by Poco
ああ、言い忘れた。
softyaさんが既に指摘していますが、
enemy_tのリストは環状構造なのでしょうか?
それとも非環状構造なのでしょうか?
環状構造の場合、一番最初にリストに要素を追加する際に、環状構造を
作っているようには見えないです(softyaさんもhead=&enemyのアドレスをp->nextあるいはtmp->nextに代入している場所が見当たりませんが?、と指摘されています)。
非環状構造であれば、for(p=head->next;p!=head;p=p->next)で終端(NULL)のチェックをやっていません。
このfor文はリストが環状であることを前提としています。
データ構造に矛盾がありませんか?
Re:不明なポインタへのアクセス
Posted: 2009年10月27日(火) 06:52
by ひよこ
環状構造って循環するってことですよね。まず、自分のわかっていることを教えます。
tmp = (enemy_t*)malloc(sizeof(enemy_t));で
新しいenemy_tリストを作って、そこに入力されたアドレスをつなぎます。
tmp->next=p->next;tmpのnextを入力されたアドレスにします。
p->next=tmp;入力されたアドレスのnextもつなぎます。
そして引数わたしです。
一応NULLをfor文に入れたらポインタエラーは出なくなりました。
Re:不明なポインタへのアクセス
Posted: 2009年10月27日(火) 13:04
by softya
リストのつながりと表示処理に疑問があるんですが。
現状、こうリスト接続されています。
[head->next]->[p->next]->[p->next]->[p->next]->NULL
で、表示処理は[head->next]から始めてますので、headの表示処理が行われていないです。
そういう意味でも、デバッガでちゃんとどの構造体を使っているかトレースしてください。
ちなみにダンプって言葉は、構造体のアドレスや各メンバの全情報を表示するって意味で使ってます。
トレースするときに、どの構造体がどのアドレスなのかを把握するために各構造体のアドレスだけでも表示しておいたほうが良いでしょう。
Re:不明なポインタへのアクセス
Posted: 2009年10月27日(火) 16:23
by ひよこ
しかし、与えられたアドレス(head)は描かれているんです。
サイトで勉強して、そこにあったもの+他のサイトというソースなので
どうして描かれるかどうかはよくわからないのです。
というよりいつも普通に登録できていたのになぜ、ポインタエラーになったのかって
ことがわかりません。
Re:不明なポインタへのアクセス
Posted: 2009年10月27日(火) 17:20
by softya
あっ分かりました。
headの構造体はnextしか使われていません。
つまり、enemy_t enemyのnextしか使われていない事になります。
動いているいるけど無駄がある&headをストッパに使っていた事は間違いって事です。
なんにしてもリスト構造をちゃんと勉強したほうが良いと思います。
http://www005.upp.so-net.ne.jp/h-masuda ... ist02.html
http://www9.plala.or.jp/sgwr-t/c/sec15-5.html
http://www.geocities.jp/ky_webid/algorithm/010.html
http://www.wakhok.ac.jp/~kanayama/C/02/index.html
お好きなサイトからどうぞ。
今後のことを考えて、アルゴリズムとデータ構造は一通り知っていた方がゲーム作るときには役立ちますよ。
それと前にも書きましたが、クラスを含む構造体をmemset(&enemy,0,sizeof(enemy_t));
でクリアしたりすることは動作未定義だったと思いますので、正常に動く保障はありません。私にとっては動いているのが不思議。
それと同じで、クラスを含む構造体をmallocで確保してますが、これも動作未定義だと思います。newで領域を確保するするようにしてください。コンストラクタやデストラクタが動かないので本来はやってはいけない事だと思います。
Re:不明なポインタへのアクセス
Posted: 2009年10月27日(火) 17:28
by ひよこ
っていうことはまさか、クラスを含む構造体を使っていたということが原因ってこともあるんですか。
もう一度勉強させていただきます。
Re:不明なポインタへのアクセス
Posted: 2009年10月27日(火) 18:21
by softya
異常終了の原因は、p!=headでループしていた事です。
>for(p=head->next;p!=head;p=p->next){
最初に提示されたプログラムでは、pのアドレスとheadのアドレスが一致するはずはありませんよね?
理解してもらっていますか?
このプログラムでは、pには最終的にNULLが入りますので、
if(p->flag==1)でポインタがNULLなのでエラーになるに決まっています。
これはリスト構造の理解不足で起こったバグですので、ちゃんとリスト構造を理解してくださいって話になったわけです。
どこかのサイトを参考にされたのか分かりませんが、安易なソース引用はバグを内包するだけで何も良い事はありません。ソースをちゃんと理解するか、根本的なアルゴリズムを理解する事が大事です。参考にしたサイトのコードがバグっている可能性は十分にあります。
Re:不明なポインタへのアクセス
Posted: 2009年10月27日(火) 18:46
by ひよこ
softyaさん、できたらp!=headにならない理由と最終的にNULLになる理由を教えてください。
Re:不明なポインタへのアクセス
Posted: 2009年10月27日(火) 19:03
by softya
>softyaさん、できたらp!=headにならない理由と最終的にNULLになる理由を教えてください。
うーん。
ちゃんとデバッガで追いかければ整理できる問題のですので、メモをしながらデバッガで追いかけてみてください。そのほうが私が教えるより勉強になります。
メモする情報は、操作する構造体のアドレスと構造体のnextに入っている値です。
注目すべき構造体は、インスタンス上のenemy_t enemy;と
void enemy_data::Init()
void enemy_data::enemy_enter()
void enemy_data::enter_enemy()
の各関数で扱っている時の構造体の情報ですね。
で、最終的に
void enemy_data::graph_enemy()からvoid enemy_data::enemy_graph()でどの様に構造体のリストを辿るか書き出してみてください。
デバッグの勉強にもなるので、自力で解析することをお勧めします。
Re:不明なポインタへのアクセス
Posted: 2009年10月27日(火) 21:30
by ひよこ
デバッガで調べた?値なんですが・・・・わかりません。
Draw,
head = 0x0012fe24 {next=0x02421ac8 x=0.00000000000000000 y=0.00000000000000000 ...}
head->next = 0x02421ac8 {next=0x02421c78 x=50.000000000000000 y=30.000000000000000 ...}
(head->next)->next = 0x02421c78 {next=0x02421ba0 x=50.000000000000000 y=33.000000000000000 ...}
((head->next)->next)->next= 0x02421ba0 {next=0x00000000 x=50.000000000000000 y=33.000000000000000 ...}
enemy_enter,
enemy = {next=0x02421ac8 x=0.00000000000000000 y=0.00000000000000000 ...}
enemy.next = 0x02421ac8 {next=0x02421c78 x=50.000000000000000 y=30.000000000000000 ...}
Re:不明なポインタへのアクセス
Posted: 2009年10月27日(火) 21:52
by softya
まず、これを見て分かる事を解説します。
>head = 0x0012fe24 {next=0x02421ac8 x=0.00000000000000000 y=0.00000000000000000 ...}
headには、nextポインタだけが有効で、構造体のその他の部分には値が入っていませんよね?
つまり、enemyの情報を保持する構造体としての役割を果たしていません。
さらに
>head->next = 0x02421ac8 {next=0x02421c78 x=50.000000000000000 y=30.000000000000000 ...}
>(head->next)->next = 0x02421c78 {next=0x02421ba0 x=50.000000000000000 y=33.000000000000000 ...}
>((head->next)->next)->next= 0x02421ba0 {next=0x00000000 x=50.000000000000000 y=33.000000000000000 ...}
3つの構造体があるのが分かりますね。headと合わせて四つです。
プログラムで敵情報は次のように
enemy_enter(0,50,30,0,PI/2,0,0,0,0);
enemy_enter(1,50,30,3,PI/2,0,0,0,0);
enemy_enter(1,50,30,3,PI/2,0,0,0,0);
3つ作っている分けですから、構造体が1つ余分に使われていて勿体無いって事になります。
で、(((head->next)->next)->next)->nextのポインタがNULL(0x00000000)になっています。つまりリストの終端はNULLになるプログラムと言える分けですね。決して終端がheadではありません。
じゃあ、このNULLがどんなタイミングで、ソースコードの何処で代入されたか分かりますか?デバッガで調べてみてください。構造体のアドレスは0x02421ba0です。
Re:不明なポインタへのアクセス
Posted: 2009年10月27日(火) 22:57
by ひよこ
ソースコードの何処で代入されたか分かりますか?デバッガで調べてみてください。
>>そういう機能ってありますか?
>>それとも地道に探すってことですか?
ひとまず、アドレスを探ってみましたが、出てきませんでした。
Re:不明なポインタへのアクセス
Posted: 2009年10月27日(火) 23:24
by Poco
> >>そういう機能ってありますか?
あります。
例えば、ある変数が変化ことを検出する機能や、ある変数がある値になったことを
検出する機能等があります。
「ブレイクポイント」とお使いのデバッガをキーワードに
Webで検索でもしてください。
> >>それとも地道に探すってことですか?
そんなに長いコードではないので、ステップ実行で地道に探すことをお勧めします。
Re:不明なポインタへのアクセス
Posted: 2009年10月27日(火) 23:53
by softya
データの変化時のブレークも可能ですが、ExpressEdtionだと出来ないかもしれません。
今回は簡単なので地道にやりましょう。でも、プログラムコードを理解するためには、この地道な作業が重要なんですよ。
方法として、nextに値を代入しているところは数箇所に絞れますよね。その全てにブレークポイントを設定するすれば、NULLを代入している場所とタイミングはつかめると思いますが、どうでしょうか?
今回のリスト構造の接続の状態を次の様に整理してみてください。
私の理想とするリスト構造だと次のように接続されてほしいです。
(1)enemy_enter(0,50,30,0,PI/2,0,0,0,0);で呼び出される。
(2)headであるenemy_t enemy;に(1)の値が設定される。nextポインタにNULLを設定。
(3)enemy_enter(1,50,30,3,PI/2,0,0,0,0);で呼び出される。
(4)enemy_t構造体をnewで確保する。これを構造体1と呼ぶ事にする。
(5)構造体1に(3)の値を設定する。構造体1のnextポインタはNULLを設定。
(6)head->nextに構造体1のアドレスを代入。
(7)enemy_enter(1,50,30,3,PI/2,0,0,0,0);で呼び出される。
(8)enemy_t構造体をnewで確保する。これを構造体2と呼ぶ事にする。
(9)構造体2に(7)の値を設定する。構造体2のnextポインタはNULLを設定。
(10)構造体1->nextに構造体2のアドレスを代入。
以上です。この処理で作られたリスト構造だと
(head->next)->(構造体1->next)->(構造体2->next)->NULL
と接続されます。
実際には、ひよこさんのプログラムではこんな形で接続されていませんので自分のプログラムで接続されている形を描き出してみてくださいね。
Re:不明なポインタへのアクセス
Posted: 2009年10月28日(水) 10:42
by softya
ひよこさんの過去ログを見てみたのですが、前にも似た内容で質問されてますね。
根本的な問題は、前回のときに中途半端な理解のまま終えてしまったことにあると思います。
kazuoniさんにも指摘されていますが、自分でデバッグできる力を身につけることを目標に今回がんばりましょう!
デバッグする力を身につけないと、これ以上大きなプログラムを組むことは出来ませんからね。今回のバグはすごく簡単なレベルですが、本格的に組みだすとポインタが有らぬ所を指したりして変数値を書き換えるバグなどやっかいなバグにぶち当たります。
私は仕事で、この手のバグで4日間プログラムを追いかけ続けた事がありますね。分かってみると他の人の作ったバグでしたが(^^ゞ
バグを取るコツは、経験に基づく勘だけが頼りですからね。経験が全てです。
Re:不明なポインタへのアクセス
Posted: 2009年10月28日(水) 16:36
by ひよこ
よく考えてみると初期化のせいで、nextがNULLになってそのまま代入していくのがいけないことなのですよね?
Re:不明なポインタへのアクセス
Posted: 2009年10月28日(水) 17:07
by softya
>よく考えてみると初期化のせいで、nextがNULLになってそのまま代入していくのがいけないことなのですよね?
うーん。
じゃあ、NULLじゃなくheadのアドレスを代入すれば良いのかと言うとそうでも無いですよね。
今回の問題点を整理してみましょう。
・NULLで終わるリスト構造だったのに、headをストッパとして使っていた。
・head構造体であるenemy_t enemy;はnextポインタとしてしか使っていない。データ領域として無駄。
・最終nextの指す先がheadにするとリスト構造としては不自然というか、循環である必要も無いのに循環している事になるが、enemy_t enemy;には値が入っていないので循環リストとしても不自然。
と言う事で数々の問題を残しています。
今後のためにも、しっかりとした構造で無駄が無く、バグの原因とならないリスト構造に直すべきです。今回は循環している必要も無いので、ストッパとしては定番のNULLが良いでしょう。それと値が入っていないenemy_t enemy;は無駄なのでリストに組み込む時は値を入れる様にすべきですね。リスト構造のアルゴリズムが理解できているなら直せるはずですよ。
それとちゃんとリスト構造が出来上がっているか、デバッガでチェックするようにしてください。確かめもせず動いたらOKは間違いの元なので確認を必ずしてください。リスト構造の勉強のためですから必ず守ってくださいね。
Re:不明なポインタへのアクセス
Posted: 2009年10月28日(水) 19:06
by ひよこ
head構造体であるenemy_t enemy;はnextポインタとしてしか使っていない。データ領域として無駄。
>>これってどのようにすればいいのでしょうか?
一回勉強して改造してきたソースなのですが、alldelete_enemy();を使うとポインタエラーが出てしまいます。ポインターエラーには対処できたと思っていたのですが?
ソース
struct enemy_t
{
int x; /* 具体的なデータ */
int y;
int cnt;
//double x,y;
double angle;
int state;
int spd;
struct enemy_t* next; /* 次の要素へのポインタ */
};
struct enemy_t head; /* リストの先頭要素(ダミー) */
static int enter;
/* プロトタイプ宣言 */
void eneter_enemy(int x,int y){
struct enemy_t * p, *q, *new_enemy;
p = head.next; /* 先頭要素の次の要素のアドレス */
q = &head; /* 先頭要素のアドレス */
while( p!=NULL)//一番後ろに
//抜けるときはqがNULLになる前,pはNULL
{
q = p; /* 追加位置の直前の要素のnextを後で設定するために、追加位置の直前の要素のアドレスを記憶しておく */
p = p->next; /* 次の要素へ進む */
}
/* 新しいリストのためにメモリ領域を確保する */
new_enemy = (enemy_t*)malloc(sizeof(struct enemy_t));
if( new_enemy == NULL )
{
puts( "メモリ不足" );
return;
}
new_enemy->x=x;
new_enemy->y=y;
new_enemy->next=p;//NULLになる
q->next=new_enemy;//リスト作成する前の最後のnextに新しく作ったリストをつなぐ
enter++;
}
void delete_enemy()
{
struct enemy_t * p, *q;
p = head.next; /* 先頭要素の次の要素のアドレス */
q = &head; /* 先頭要素のアドレス */
while(p!=NULL)//一番後ろに
//抜けるときはqがNULLになる前,pはNULLか、値に引っかかったとき
{
if(p->x < -30 || p->x >670 || p->y < -30 || p->y > 510)
break;
q = p; /* 追加位置の直前の要素のnextを後で設定するために、追加位置の直前の要素のアドレスを記憶しておく */
p = p->next; /* 次の要素へ進む */
}
if( p != NULL ){ /* 一致する値がないままリストの末尾まで来た */
q->next = p->next; /* 削除する要素の直前の要素のnextポインタを再設定 */
free( p ); /* 要素はmallocで確保されているのでfreeで解放する */
}
}
void draw_enemy(){
struct enemy_t *p;
p=head.next;
while(p!=NULL)//一番後ろに
{
DrawFormatString(0,0,GetColor(255,255,255),"%d,x=%d,y=%d", p,p->x,p->y );
p=p->next;
}
}
void alldelete_enemy(){
enemy_t *p,*tmp;
if(head.next!=NULL){
for (p=head.next;p!=NULL;)
{
tmp = p;
p=p->next;
free(tmp);
}
//p->next=NULL;//pがheadと一緒のアドレスになったときにその先のアドレスをNULLにする。
}
}
Re:不明なポインタへのアクセス
Posted: 2009年10月28日(水) 20:06
by softya
>head構造体であるenemy_t enemy;はnextポインタとしてしか使っていない。データ領域として無駄。
>>これってどのようにすればいいのでしょうか?
headがダミーなら構造体先頭のポインタだけにすれば良いと思いますよ。
>一回勉強して改造してきたソースなのですが、alldelete_enemy();を使うとポインタエラーが出てしまいます。ポインターエラーには対処できたと思っていたのですが?
私が調べても良いですが、デバッガでトレースされましたか?
リストの接続がどうなっているか、その情報が欲しいのですが。
Re:不明なポインタへのアクセス
Posted: 2009年10月28日(水) 20:24
by ひよこ
登録時にp!=NULLで最後まで行って、登録された順になると思います。
トレース?ってなんですか?
Re:不明なポインタへのアクセス
Posted: 2009年10月28日(水) 20:42
by softya
>登録時にp!=NULLで最後まで行って、登録された順になると思います。
今回のプログラムで、前のこんなのをもう一度おねがいします。
>head->next = 0x02421ac8 {next=0x02421c78 x=50.000000000000000 y=30.000000000000000 ...}
>(head->next)->next = 0x02421c78 {next=0x02421ba0 x=50.000000000000000 y=33.000000000000000 ...}
>((head->next)->next)->next= 0x02421ba0 {next=0x00000000 x=50.000000000000000 y=33.000000000000000 ...}
>トレース?ってなんですか?
追いかける&命令の実行された形跡をたどるってことです。
Re:不明なポインタへのアクセス
Posted: 2009年10月28日(水) 20:55
by ひよこ
リスト.exe の 0x0054e9cc で初回の例外が発生しました: 0xC0000005: 場所 0xfeeefef2 を読み込み中にアクセス違反が発生しました。
リスト.exe の 0x0054e9cc でハンドルされていない例外が発生しました: 0xC0000005: 場所 0xfeeefef2 を読み込み中にアクセス違反が発生しました。
リストを消したはずなのにありますね。
draw_enemy
head.next = 0x01f13f98 {x=-17891602 y=-17891602 cnt=-17891602 ...}
(head.next)->next = 0xfeeefeee {x=??? y=??? cnt=??? ...}
命令の実行された形跡(実行したこと)
eneter_enemy(20,20);->delete_enemy();->draw_enemy();->alldelete_enemy();
DrawFormatString(0,0,GetColor(255,255,255),"%d,x=%d,y=%d", p,p->x,p->y );でひっかかりました。
追いかけるっていうのは、デバッガの値をですね。
Re:不明なポインタへのアクセス
Posted: 2009年10月28日(水) 21:58
by softya
自分で答えを見つけてもらうって趣旨で説明してますので、回りくどくなりますがご容赦ください。
>(head.next)->next = 0xfeeefeee {x=??? y=??? cnt=??? ...}
この時点でnextのポインタの値が不正ですね。
void eneter_enemy(int x,int y)の関数の中で間違っていますので、ステップ実行して原因を探ってください
一行ずつ動作を確認すれば分かると思います。めんどくさいなと思うかもしれませんが、これがデバッグです。プロでも同じ事をするしかないんですよ。
Re:不明なポインタへのアクセス
Posted: 2009年10月28日(水) 23:05
by ひよこ
あっ、すでに一回目でnew_enemyおかしくなってます、qもです。しかし、どこが悪いかさっぱりなんです。
Re:不明なポインタへのアクセス
Posted: 2009年10月28日(水) 23:31
by softya
>あっ、すでに一回目でnew_enemyおかしくなってます、qもです。しかし、どこが悪いかさっぱりなんです。
じゃあ、解説してみますね。
void eneter_enemy(int x,int y){
struct enemy_t * p, *q, *new_enemy;
p = head.next; ← 生成しただけのインスタンスのメンバ変数は値が不定です。なので、head.nextの値はNULL以外の可能性が高いです。構造体をウォッチしてみると分かると思います。
q = &head;
while( p!=NULL) ←ここで不定なpがNULLと聞いていますので、この時点で既にバグった動作になります。
{
q = p; ← 不正なpを代入しています。
p = p->next; ← 不正なpポインタで構造体を参照していますので、どうなるかは神のみぞ知るです。
}
とここまでにして置きます。
対処方法。
インスタンス生成時に不定なメンバ変数は、コンストラクタで初期化するようにしてください。クラスを含んでいる構造体はmemcpyは使用禁止です。含んでいなければ良いですが、後々クラスを構造体に含める可能性があるなら全部の項目をマメに初期化して下さい。
それと前に言いましたが、C++ではmallocよりもnewをお勧めします。あえてmallocを使う必然性は何もありませんので、newしたときに自動的にクラスのコンストラクタを呼んでくれるC++として正しい動作をするnewを日ごろから使うように心がけてください。たとえ構造体にクラスを含んでいなくてもです。
Re:不明なポインタへのアクセス
Posted: 2009年10月28日(水) 23:50
by conio
ポインタはNULLで初期化して、
使わなくなったときにもNULLを入れておいたほうが良いですよ。
以前、初期化し忘れのポインタを下記の様にif文でdeleteしようとして、
プログラムが壊れた経験があります。
(異常終了)
-----------------
if(p != NULL) { //←初期化していなかったら、この条件文が成立する。
delete p; //←不定の場所をdelete
p = NULL;
}
-----------------
Re:不明なポインタへのアクセス
Posted: 2009年10月28日(水) 23:54
by ひよこ
生成しただけのインスタンスのメンバ変数は値が不定です
>>不定ってことは値が変わるってことですか?
ただいま、もう一回値を見ていたら、
p = head.next; /* 先頭要素の次の要素のアドレス */
p = 0xcccccccc {x=??? y=??? cnt=??? ...}
head.next = 0x00000000 {x=??? y=??? cnt=??? ...}
と代入する式なのにアドレスが違うのも、不定のせいですか?
まだまだC++はほとんど勉強してませんので、勉強してきます。
こういう感じのクラスでいいんですか?
private:enemy_t enemyをコンストラクタで初期化してから使う。ということですか?
Re:不明なポインタへのアクセス
Posted: 2009年10月29日(木) 00:11
by softya
>p = head.next; /* 先頭要素の次の要素のアドレス */
>p = 0xcccccccc {x=??? y=??? cnt=??? ...}
>head.next = 0x00000000 {x=??? y=??? cnt=??? ...}
>と代入する式なのにアドレスが違うのも、不定のせいですか?
不定なので毎回値が変わる可能性がありますね。
つまり何が入っているかは運次第です。
もし書き換え可能なメモリアドレスがたまたま入っていると、プログラムのほかの変数を壊す大惨事のバグを引き起こします。
>まだまだC++はほとんど勉強してませんので、勉強してきます。
今のところC++以前に、デバッグの経験不足です。
今回の件も構造体の未初期化にあることに自力で気づいて欲しかったところですね。
構造体の変数をちゃんと確認していたら、不正な値が入っている事に気づいたはずですからね。
デバッグは経験が全てですので、バグっているプログラムは格好の勉強ネタですよ。
>こういう感じのクラスでいいんですか?
クラスが敵情報をリスト構造で持つ事は、自機との当たり判定とかをどうするのかとか色々悩ましいところですが、一度自分なりに作ってみて問題があれば変えればよいだけです。そのまま。突っ走ってみてください。もし失敗でも、それも大事な経験ですよ。
>private:enemy_t enemyをコンストラクタで初期化してから使う。ということですか?
そういうことですね。
ただ前にも行った通り、値を代入しないheadは無駄なのでenemy_t *head_enemy;としてしまってhead構造体は不要なんじゃないかと思います。
Re:不明なポインタへのアクセス
Posted: 2009年10月29日(木) 17:13
by ひよこ
アドレスがおかしいようなのですが、どうすればいいのでしょうか?
struct enemy_t
{
int cnt;
double x,y;
double angle;
int state;
int spd;
struct enemy_t* next; //次の要素へのポインタ
};
class Enemy
{
private:
enemy_t *head_enemy;
public:
Enemy(); // コンストラクタの宣言
//~Enemy();
void Get();
};
void Enemy::Get(){
}
// コンストラクタ
Enemy::Enemy()
{
//memset(head_enemy,0,sizeof(enemy_t));
head_enemy->next=NULL;//nextを初期化
}
Re:不明なポインタへのアクセス
Posted: 2009年10月29日(木) 18:41
by 初級者
head_enemy には適切な値が入っているのでしょうか?
クラスがからむと話がややこしくなりそうです。
まずは自己参照型構造体「だけ」を使って、
リスト構造の基礎から学習なさってはいかがでしょうか。
クラスに手を出すのは後からでもよいと思います。
Re:不明なポインタへのアクセス
Posted: 2009年10月29日(木) 18:45
by ひよこ
head_enemy には適切な値
>>はいっていると思いますが・・・何もしてない地点でポインタエラーです。
Re:不明なポインタへのアクセス
Posted: 2009年10月29日(木) 18:49
by softya
何度も言いますが、デバッガで動作を確認してください。
>head_enemy->next=NULL;//nextを初期化
に問題があります。
ひよこさんが推測できる「アドレスがおかしい」問題の原因って何だと思いますか?
これは、C言語レベルの問題でC++レベルの問題ではありません。
ポインタの基本中の基本レベルの間違いですよ。
Re:不明なポインタへのアクセス
Posted: 2009年10月29日(木) 18:59
by ひよこ
まさかのアドレスを代入していないという基本のことですか?
Re:不明なポインタへのアクセス
Posted: 2009年10月29日(木) 19:07
by softya
>まさかのアドレスを代入していないという基本のことですか?
enemy_t *head_enemy;
はポインタですが、コードを見る限りポインタが指すべき構造体の実体は何処にもありません。
これは、デバッガでhead_enemyメンバ変数の値を確認すれば分かる事ですね。
本当にクラスが原因で混乱しているなら、C言語でリスト構造を処理する関数から作ってみる事をお勧めします。
Re:不明なポインタへのアクセス
Posted: 2009年10月29日(木) 19:12
by ひよこ
実体がないので、こういう感じでいいのでしょうか?
構造体の下に
enemy_t enemy;
コンストラクタ
head_enemy=&enemy;
こういう感じにすると無駄がないのでしょうか?
一回Cに戻ってリストがんばります。
Re:不明なポインタへのアクセス
Posted: 2009年10月29日(木) 19:25
by softya
実体を後で確保する前提ですので不定な値のままだとまずいので、
コンストラクタでは、
head_enemy=NULL;
で良いと思います。
で動作確認していませんが、
// 実態を作る。
enemy_t *new_enemy;
new_enemy = new enemy_t;
new_enemy->next=NULL;
// 最初の処理なら。
if( head_enemy==NULL ) {
// 最初の敵データ
head_enemy = new_enemy;
} else {
// リストの最後を探す。
enemt_t *now_enemy = head_enemy;
while( now_enemy->next!=NULL ) {
now_enemy = now_enemy->next;
}
// リストの最後に接続する。
now_enemy->next = new_enemy;
}
って感じでリストに接続します。
Re:不明なポインタへのアクセス
Posted: 2009年10月29日(木) 20:08
by ひよこ
なるほど、新しく実体を作ってそのアドレスをポインタに代入するということですか?
Re:不明なポインタへのアクセス
Posted: 2009年10月29日(木) 20:37
by ひよこ
new_enemy->x=300;new_enemy->y=290;softyaさんのコードを考えて、左のように代入して、
内容を表示させようとしたら、0とでてしまいました。
enemy_tの構造体のdouble x,y;をint x,y;にしたら表示されるのですがどうしてでしょうか?
enemy_t *p;
int count=0;
p=head_enemy;
if(p!=NULL)
while(p!=NULL){
DrawFormatString(0,count*20,GetColor(255,255,255),"%d",p->x);
p=p->next;
count++;
}
Re:不明なポインタへのアクセス
Posted: 2009年10月29日(木) 20:56
by softya
Re:不明なポインタへのアクセス
Posted: 2009年10月29日(木) 22:42
by ひよこ
内部の値の範囲が全然違いますね。
敵が画面の外に出たら消すというものなんですが、
最初のpで
p = head_enemy->next;だと1番最初の弾が消せないし
p = head_enemy;とすると前のポインタで困るしどうすればいいでしょうか?
void delete_enemy()
{
enemy_t *p, *q;
if(head_enemy!=NULL){
p = head_enemy; /* 先頭要素の次の要素のアドレス */
q = head_enemy; /* 先頭要素のアドレス */
while(p!=NULL)//一番後ろに
//抜けるときはqがNULLになる前,pはNULLか、値に引っかかったとき
{
if(p->x < -30 || p->x >390 || p->y < -30 || p->y > 510)
break;
q = p; /* 追加位置の直前の要素のnextを後で設定するために、追加位置の直前の要素のアドレスを記憶しておく */
p = p->next; /* 次の要素へ進む */
}
if( p != NULL ){ /* 一致する値がないままリストの末尾まで来た */
q->next = p->next; /* 削除する要素の直前の要素のnextポインタを再設定 */
delete( p ); /* 要素はmallocで確保されているのでfreeで解放する */
}
}
}
Re:不明なポインタへのアクセス
Posted: 2009年10月30日(金) 00:20
by softya
私流に書かせてもらいますね。
void delete_enemy()
{
enemy_t *check_enemy = head_enemy;
enemy_t *before_enemy = NULL;
while( check_enemy != NULL ) {
enemy_t *next_enemy = check_enemy->next;
if(check_enemy->x < -30 || check_enemy->x >390 || check_enemy->y < -30 || check_enemy->y > 510) {
// 削除
if( before_enemy == NULL ) {//先頭か
head_enemy = next_enemy;
} else {
before_enemy->next = next_enemy;
}
delete check_enemy;
} else {
before_enemy = check_enemy;
}
check_enemy = next_enemy;
}
}
動作確認してませんので、バグがあったらごめんなさい。
Re:不明なポインタへのアクセス
Posted: 2009年10月30日(金) 18:05
by ひよこ
softyaさんのコードを勉強しましたがけっこう難しいですね。
しかし、こんな私に適切なアドバイスありがとうございます。
softyaさんって専門学校の学生ですか?無駄がないもので・・・
というより独学で勉強したというならすごすぎです。
Re:不明なポインタへのアクセス
Posted: 2009年10月30日(金) 18:19
by softya
>softyaさんって専門学校の学生ですか?無駄がないもので・・・
>というより独学で勉強したというならすごすぎです。
病気療養中の社会人です。一応プロですね。
専門学校も卒業もしてますが、アルゴリズムや書き方については、大半は独学ですね。
C言語も独学ですよ。仕事場では、入門書渡されて、読んで分かんなかったら聞いて!って感じでした。
まぁ、このぐらい分かんない人間には用が無いって職場でしたが(昔のゲーム業界です。今の仕事は違います)。
Re:不明なポインタへのアクセス
Posted: 2009年10月30日(金) 18:45
by ひよこ
そうなんですか。
私も独学です。何もできませんが・・・・
ポインタ関係をできるだけ覚えたいのですが、
softyaさんはどのようにして覚えました?
Re:不明なポインタへのアクセス
Posted: 2009年10月30日(金) 19:08
by softya
>ポインタ関係をできるだけ覚えたいのですが、
>softyaさんはどのようにして覚えました?
元々アセンブラをやっていたので、ポインタを理解するのに苦労しなかったんですよ。
お役に立てず申し訳ない。
ただ、経験を積むとポインタ変数に入っている値(アドレス)を見ただけで、やばいアドレスか正しいそうな
アドレスかをある程度判別できるようになります。
mallocやnewで確保したアドレスとローカル変数のアドレスには、それぞれクセがありますし、未初期化のポインタも大半は特定のクセのある値(アドレス)が入っている可能性が高いです。いつもデバッガで変数など追いかける事が大事ですね。
あと、複雑なデータ構造は紙にでも図を描いたほうが良いと思いますね。図を書くと更にイメージが固まりますし、もし図を書けないなら、そのアルゴリズムが理解出来ていないって事です。
Re:不明なポインタへのアクセス
Posted: 2009年10月30日(金) 19:25
by ひよこ
わかりました。
ポインタ理解でわからないことがあったら質問させていただきます。
このスレッドのみなさん、そしてsoftyaさんありがとうございました。