ページ 1 / 1
循環リストについてなんですが・・
Posted: 2009年9月23日(水) 14:24
by ひよこ
最近興味を持ったことについて勉強をしています。
循環リストについてなんですが・・
「ためになるホームページ」のサンプルを改造していて
なぜ下のソースの実行結果の一番最後がbullet.x,bullet.yの内容なのでしょうか?
C言語はほとんどできません。
#include <stdio.h>
#include <malloc.h>
struct bullet_t{
struct bullet_t *next; /* 次の構造体変数のアドレス */
int x;
int y;
};
bullet_t bullet;
void List_print(bullet_t *head){
bullet_t *p, *tmp;
for(p=head->next;p!=head;)
{
tmp=p;
p=tmp->next;
printf("%d\n",p->x);
printf("%d\n",p->y);
}
}
int bullet_data_set(bullet_t *p,int x,int y){
bullet_t *tmp;
tmp = (bullet_t*)malloc(sizeof(bullet_t));
//要素が存在した場合
if(p->next!=NULL)
{
tmp->next=p->next;
p->next=tmp;
tmp->x=x;
tmp->y=y;
}
else//要素がない場合
{
tmp->next=p;//pのアドレスをtmp->nextへ
p->next=tmp;//tmpのアドレスをp->nextへ
tmp->x=x;
tmp->y=y;
}
return 0;
}
int main(){
bullet.next=NULL;
bullet.x=0;
bullet.y=0;
bullet_data_set(&bullet,80,90);//一番最後,後ろに行くほど先描く
bullet_data_set(bullet.next,60,80);
bullet_data_set(bullet.next,80,120);
List_print(&bullet);//リスト内容を描く
getchar();//一時停止
return 0;
}
Re:循環リストについてなんですが・・
Posted: 2009年9月23日(水) 16:49
by kazuoni
>なぜ下のソースの実行結果の一番最後がbullet.x,bullet.yの内容なのでしょうか?
?
そのように関数が組んであるからですが・・・?
たぶんこういうことがやりたいのでは?っと勝手に予想してみます^^;
void List_print(bullet_t *head){
bullet_t *p, *tmp;
for(p=head->next;p!=head;p=p->next)
{
tmp=p;
printf("%d\n",p->x);
printf("%d\n",p->y);
}
}
Re:循環リストについてなんですが・・
Posted: 2009年9月23日(水) 19:05
by ひよこ
kazuoniさん
たぶんこういうことがやりたいのでは?っと勝手に予想してみます^^;
>>実行結果が自分のおもったとおりになってます。ありがとうございます。
やりたいことは、弾の表示負荷を減らすため、リストを勉強しています。
弾を描き、その弾を全部一度に消す。そしてまた弾を描くということがしたいです。
kazuoniさんのすばらしいコードを変えていっているのですが、
リスト.exe の 0x6485c2b2 で初回の例外が発生しました: 0xC0000005: 場所 0xfeeefee8 を読み込み中にアクセス違反が発生しました。
リスト.exe の 0x6485c2b2 でハンドルされていない例外が発生しました: 0xC0000005: 場所 0xfeeefee8 を読み込み中にアクセス違反が発生しました。というエラーが出てきました。
どういう原因なんでしょうか?
下ソースです。
#include <stdio.h>
#include <malloc.h>
struct bullet_t{
struct bullet_t *next; /* 次の構造体変数のアドレス */
int x;
int y;
};
bullet_t bullet;
void List_print(bullet_t *head){
bullet_t *p, *tmp;
for(p=head->next;p!=head;p=p->next)
{
tmp=p;
printf("%d\n",p->x);
printf("%d\n",p->y);
}
}
int bullet_data_set(bullet_t *p,int x,int y){
bullet_t *tmp;
tmp = (bullet_t*)malloc(sizeof(bullet_t));
//要素が存在した場合
if(p->next!=NULL)
{
tmp->next=p->next;
p->next=tmp;
tmp->x=x;
tmp->y=y;
}
else//要素がない場合
{
tmp->next=p;//pのアドレスをtmp->nextへ
p->next=tmp;//tmpのアドレスをp->nextへ
tmp->x=x;
tmp->y=y;
}
return 0;
}/*
int deletebullet(bullet_t* p)
{
bullet_t *tmp;
if(p->next==NULL)
return -1;
tmp = p->next;
p->next = tmp->next;
free(tmp);
return 0;
}*/
void allDeletebullet(bullet_t *head)
{
bullet_t *p, *tmp;
for (p=head->next;p!=head;p=p->next)
{
tmp = p;
free(tmp);
}
}
int main(){
bullet.next=NULL;
bullet.x=0;
bullet.y=0;
bullet_data_set(&bullet,80,90);//一番最後,後ろに行くほど先描く
bullet_data_set(bullet.next,60,80);
bullet_data_set(bullet.next,80,120);
List_print(&bullet);//リスト内容を描く
allDeletebullet(&bullet);
List_print(&bullet);//リスト内容を描く
getchar();//一時停止
return 0;
}
Re:循環リストについてなんですが・・
Posted: 2009年9月23日(水) 19:48
by kazuoni
>どういう原因なんでしょうか?
えっと、原因は、tmpにpのノードのアドレスを代入し、
freeしてしまうと、pのアドレス先を解放したのと同じになるので、
解放後、pをnextすると、不正のアドレスを指すことになり、
そのアドレスを解放するときに解放ができていないのが原因です。
こんな感じになるかと
void allDeletebullet(bullet_t *head)
{
bullet_t *p, *tmp;
for (p=head->next;p!=head;)
{
tmp = p;
p=p->next;
free(tmp);
}
p->next=NULL;
}
あと、リストはまず紙に書いて考えることをお勧めします。
そして、組み、あとはひたすらデバッグで状況を追って行ってください。
そうすれば早くリスト構造の仕組みが理解でき、
はかどるかと思いますよ。
ネットでいろいろ調べてみてもいいかも知れません。
Re:循環リストについてなんですが・・
Posted: 2009年9月23日(水) 20:46
by ひよこ
そうですね、紙に書いていくことから始めたいと思います。
kazuoniさんありがとうございました。
Re:循環リストについてなんですが・・
Posted: 2009年9月23日(水) 20:49
by ひよこ
解決押し忘れました。
Re:循環リストについてなんですが・・
Posted: 2009年9月23日(水) 23:10
by ひよこ
ただいま勉強してたところなんですが、(backを使わないけれど入れてみたりしてリストの勉強してました。)allDeletebullet関数直後にList_printがとおると、
不正なアドレスを指定するのでList_printにif(p->next!=NULL)をいれたら
Run-Time Check Failure #3 - The variable 'p' is being used without being initialized.
きっと初期化されてないpが使われているって意味なんだと思うのですが、
どう初期化すればいいでしょうか?
#include <stdio.h>
#include <malloc.h>
//注意!「/*:*/」は*backのアドレス関係の行であることを表します。
struct bullet_t{
struct bullet_t *next; /* 次の構造体変数のアドレス */
struct bullet_t *back;
int x;
int y;
};
bullet_t bullet;
void List_print(bullet_t *head){
bullet_t *p;
if(p->next!=NULL){//allDeletebullet関数直後に描かないようにする
for(p=head->next;p!=head;p=p->next)
{
//printf("%d\n",p->back);
printf("%d\n",p->x);
printf("%d\n",p->y);
}
}
}
int bullet_data_set(bullet_t *p,int x,int y){
bullet_t *tmp;
tmp = (bullet_t*)malloc(sizeof(bullet_t));
//要素が存在した場合
if(p->next!=NULL)
{
tmp->next=p->next;
/*:*/tmp->back=p;
p->next=tmp;
/*:*/p->back=tmp->next;
tmp->x=x;
tmp->y=y;
}
else//要素がない場合
{
tmp->next=p;//pのアドレスをtmp->nextへ
/*:*/tmp->back=p;
p->next=tmp;//tmpのアドレスをp->nextへ
/*:*/p->back=tmp;
tmp->x=x;
tmp->y=y;
}
return 0;
}
void allDeletebullet(bullet_t *head)
{
bullet_t *p, *tmp;
for (p=head->next;p!=head;)
{
tmp = p;
p=p->next;
free(tmp);
}
p->next=NULL;//pがheadと一緒のアドレスになったときにその先のアドレスを
//NULLにする。
}
int main(){
bullet.next=NULL;
bullet.x=0;
bullet.y=0;
bullet_data_set(&bullet,80,90);//一番最後,後ろに行くほど先描く
bullet_data_set(bullet.next,60,80);
bullet_data_set(bullet.next,80,120);
List_print(&bullet);//リスト内容を描く
allDeletebullet(&bullet);
List_print(&bullet);//リスト内容を描く
getchar();//一時停止
return 0;
}
Re:循環リストについてなんですが・・
Posted: 2009年9月24日(木) 00:20
by kazuoni
if(p->next!=NULL)
は
if(head->next!=NULL)
の間違えじゃないですか?
Re:循環リストについてなんですが・・
Posted: 2009年9月24日(木) 06:28
by ひよこ
できました。なんで、引数が初期化(bullet_t *head;)されて、中で同じようなこと(
bullet_t *p;)をしているのに、pじゃ、だめなんですか?
Re:循環リストについてなんですが・・
Posted: 2009年9月24日(木) 10:03
by kazuoni
※以下のコードは例えであって、
有効な関数ではないので、使わないでください。
void func(int a)
{
int b;
printf("a=%d",a);
printf("b=%d",b);
}
上に示した関数でaとbは等しいですか?
とは限らないですよね。
bは初期化されていないintです。
bullet_tは構造体ですが、intやcharと同じ「型」と思ってください。
Re:循環リストについてなんですが・・
Posted: 2009年9月24日(木) 16:50
by ひよこ
headは引数で初期化されているけど、pはつくっただけみたいなことですか?
Re:循環リストについてなんですが・・
Posted: 2009年9月24日(木) 19:58
by kazuoni
そういうことですね。
入門サイトの「関数」の項目を見ておいたほうがいいかも知れません。
まだ、理解できていないor知らない事があるかもしてません。
Re:循環リストについてなんですが・・
Posted: 2009年9月24日(木) 20:34
by ひよこ
そうだったんですか、ただいま、もし画面外に出たら弾を消すという作業をやっているのですが、
deletebullet(bullet_t *head)//外に出た弾を消すという関数を使ったとたんにアクセス違反となるのですが
どうすればいいでしょうか?おそらくアドレス関係だと思いますがどうすればいいでしょうか?
#include "DxLib.h"
#include <string.h> /* memsetを使うため */
#include <math.h>
//円周率
#define PI 3.14159265358979323846
int Key[256];
int image[16];
int GetHitKeyStateAll_2(int GetHitKeyStateAll_InputKey[/url]){
char GetHitKeyStateAll_Key[256];
GetHitKeyStateAll( GetHitKeyStateAll_Key );
for(int i=0;i<256;i++){
if(GetHitKeyStateAll_Key==1) GetHitKeyStateAll_InputKey++;
else GetHitKeyStateAll_InputKey=0;
}
return 0;
}
struct bullet_t{
struct bullet_t *next; /* 次の構造体変数のアドレス */
struct bullet_t *back; /* 前の構造体変数のアドレス(代入はしているけど未使用) */
double x,y; //座標
double spd;//スピード
double angle;//角度
int flag;
};
bullet_t bullet;
void Bullet_draw(bullet_t *head){
bullet_t *p;
if(head->next!=NULL){//allDeletebullet関数直後に描かないようにする
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);
DrawRotaGraphF( (float)p->x , (float)p->y , 1.0 , p->angle, image[8] , TRUE );
}
}
}
}
void allDeletebullet(bullet_t *head)
{
bullet_t *p, *tmp;
if(head->next!=NULL){
for (p=head->next;p!=head;)
{
tmp = p;
p=p->next;
free(tmp);
}
p->next=NULL;//pがheadと一緒のアドレスになったときにその先のアドレスを
//NULLにする。
}
}
void deletebullet(bullet_t *head)//外に出た弾を消す
{
bullet_t *p, *tmp;
//pはhead->nextからpと同じアドレスになるまでループ
if(head->next!=NULL){
for(p=head->next;p!=head;){
if(p->flag==1){
if(p->x<0 || p->x<=640 || p->y<=0 || p->y>=640){
tmp = p;//tmpをpのアドレスに
p=p->next;//pのアドレスをp->nextにする。
free(tmp);//tmp(昔のp)を解放
}
}
}
}
}
int bullet_data_set(bullet_t *p,double x,double y ,double angle,double spd){
bullet_t *tmp;
tmp = (bullet_t*)malloc(sizeof(bullet_t));
//要素が存在した場合
if(p->next!=NULL)
{
tmp->next=p->next;
/*:*/tmp->back=p;
p->next=tmp;
/*:*/p->back=tmp->next;
tmp->flag=1;
tmp->x=x;
tmp->y=y;
tmp->spd=spd;
tmp->angle=angle;
}
else//要素がない場合
{
tmp->next=p;//pのアドレスをtmp->nextへ
/*:*/tmp->back=p;
p->next=tmp;//tmpのアドレスをp->nextへ
/*:*/p->back=tmp;
tmp->flag=1;
tmp->x=x;
tmp->y=y;
tmp->spd=spd;
tmp->angle=angle;
}
return 0;
}
void ini(){
memset(&bullet,0,sizeof(bullet_t));
bullet.next=NULL;
bullet.back=NULL;
bullet_data_set(&bullet,80,90,PI,0);
}
int WINAPI WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance,LPSTR lpCmdLine, int nCmdShow ){
ChangeWindowMode(TRUE);//ウィンドウモード
if(DxLib_Init() == -1 || SetDrawScreen( DX_SCREEN_BACK )!=0) return -1;//初期化と裏画面化
LoadDivGraph( "char.png" , 16 , 4 , 4 , 32 , 32 , image );//画像を分割してimage配列に保存
ini();
bullet_data_set(bullet.next,240,77,PI/2,3);
while(ProcessMessage()==0 && ClearDrawScreen()==0 && GetHitKeyStateAll_2(Key)==0 && Key[KEY_INPUT_ESCAPE]==0){
//↑メッセージ処理 ↑画面をクリア ↑入力状態を保存 ↑ESCが押されていない
if(Key[KEY_INPUT_Z]==1)
deletebullet(&bullet);
if(Key[KEY_INPUT_X]==1)
allDeletebullet(&bullet);
Bullet_draw(&bullet);
ScreenFlip();
}
DxLib_End();
return 0;
}
Re:循環リストについてなんですが・・
Posted: 2009年9月24日(木) 23:59
by kazuoni
とりあえず、データは二つ入っていますが、
zを押すと、データは二つとも消えます。(解放)
(逸れますが、p->x<0 || p->x<=640 ...はおかしくないですか?)
その次に、データが先頭のノードのみになります。(このノードのnextは解放後何もされていない。)
そのまま、Drawするので、head->nextにアクセスし、
p->flagとすると不正なアドレスのアクセスになり、止まります。
あと、今のままだと、例ですが
A→B→C→D→A....
のリストでBのみ削除対象になると、
本体のリストはAだけになります。
A->next=C
の処理をしていないので。
今一度、リスト構造の基礎を見直してみてください。
Re:循環リストについてなんですが・・
Posted: 2009年9月25日(金) 06:53
by ひよこ
ただいま試行錯誤でやっています。
p->x<0 || p->x<=640 ...はおかしくないですか?)
>>おかしいですね、直しました。
デバックでZでdeletebulletを使うとなぜかフリーズするのですが、どうしてでしょうか
一回もprintfDxが動いた跡がありません。
void deletebullet(bullet_t *head)//外に出た弾を消す
{
bullet_t *p, *tmp;
//pはhead->nextからpと同じアドレスになるまでループ
if(head->next!=NULL){
for(p=head->next;p!=head;){
printfDx("一回きました");
if(p->flag==1){//アクセス違反
printfDx("一回きました");
if(p->x <=0 || p->x >=640 || p->y <=0 || p->y >=480){//xが0以下640以上だったら,yが0以下480以上だったら
printfDx("一回きました");
tmp = p;//tmpをpのアドレスに
p=p->next;//pのアドレスをp->nextにする。
free(tmp);//tmp(昔のp)を解放
}
}
}
}
}
Re:循環リストについてなんですが・・
Posted: 2009年9月25日(金) 08:57
by kazuoni
とまる理由、アクセス違反が起こる理由は
自分で調べれるようにならないと、
この先辛いのでまずはそれから。
環境が何か分かりませんが、VC++と仮定します。
とりあえず、止まる場所がだいたいどのあたりか、
目星をつけます。(止まるところがどこかはデバッグすればわかりますよね?)
あとはその前をひたすら追っていきます。
VC++でいう、ブレークポイントです。
行の左に赤丸が出ます。そこでは条件停止、無条件停止などさまざまな止め方があるので、
これで今は十分プログラム停止の原因がつかめます。
今回の件ですが、
ブレークポイントをはれば分かる通り、
無限ループに陥る場合があります。
(キャラが画面内にいるとき)
if(p->x <=0 || p->x >=640 || p->y <=0 || p->y >=480)
に初めがヒットせず、そのまま無限ループです。
ScreenFlipにたどり着かないので、
表示がされることはありません。
解決策は、
p=p->next;//pのアドレスをp->nextにする。
を外に出してください。
また前回も書きましたが、これだと、
deleteには不十分な構造です。
今一度、設計を見なおしてください。
Re:循環リストについてなんですが・・
Posted: 2009年9月25日(金) 18:58
by ひよこ
もう一回勉強したらできました。
これから少しずつリストについて勉強しようと思います。
kazuoniさんありがとうございました。