ページ 1 / 1
サイズの違うマップをステージが変わるごとにメモリ確保したい
Posted: 2011年3月15日(火) 13:21
by 紅いオレンジ色
はじめまして、C言語歴1年の者なのですが、今アクションゲームを作成しているのですが、サイズの違うマップを読み込みたいのですが、なかなかうまくいきません。
コード:
//ヘッダーのインクルード
#include <stdio.h>
#include <windows.h>
#include <stdlib.h>
BYTE **mapdata = { NULL };
//マップメイン関数
void MapMain(){
MapLoad( "マップ名.txt" , mapdata , 200 , 60 );
}
//マップロード用関数( 引数 1.ファイル名 2.読み込むデータ 3.読み込むデータのサイズ(横) 4.読み込むデータのサイズ(縦) )
BOOL MapLoad( char *test , BYTE **mapdata , int size_w , int size_h ){
HANDLE hFile = NULL;//ファイルハンドルの格納用
DWORD errchk;//エラーチェック用
int i=0 , j=0; //配列スコープ用
hFile = CreateFile(
test, //読み込むファイルパス
GENERIC_READ, //読込みモード
FILE_SHARE_READ, //他のファイルからの読込みを許可
NULL,
OPEN_EXISTING, //既存のファイルを開く(無ければエラー)
FILE_ATTRIBUTE_NORMAL, //通常属性
NULL );
//開けたかどうかを確認
if( hFile == INVALID_HANDLE_VALUE ){
return FALSE; //オープンに失敗していたら終了
}
//mallocを使ってメモリを動的に確保する
mapdata = (BYTE**)malloc(sizeof(BYTE*) * size_w );
for( i=0; i<size_w; i++ ){
mapdata[i] = (BYTE*)malloc(sizeof(BYTE) * size_h );
}
if( mapdata == NULL ){
printf( "メモリの確保に失敗!!\n" );
exit(-1);
}else{
printf( "メモリの確保に成功!!\n" );
}
//読み込みを行う
for( i=0; i<size_w; i++ ){
for( j=0; j<size_h; j++ ){
printf( "%d" , mapdata[i][j] );
//ファイルの読み込み
ReadFile( hFile , &mapdata[i][j] , size_w * size_h , &errchk , NULL );
if( j=size_h ){
break;
}
}
}
//ファイルポインタの位置を設定
//SetFilePointer( hFile, 0, NULL, FILE_BEGIN );
CloseHandle( hFile ); //ファイルを閉じる
//メモリを解放
for( int k=0; k<size_w; k++ ){
free( * ( mapdata + k ) );
}
free( mapdata );
return TRUE;
}
1.マップのメモリを動的に確保し
2.ファイルを読み込み
以上のように、ポインタのポインタを使ってマップ用のメモリを動的に確保しサイズの違うマップを読み込みたいのですが、実行は出来るのですが、「0x00000000 を読み取り中にアクセス違反が発生しました」となりエラーになってしまいます。
どうも調べてみると、OSのシステムがあるメモリを参照していてエラーになっているようなのですが、なぜそうなってしまうのかがわかりません。
使用中の自分のパソコンのスペックですが
OS : WindowsVista
コンパイラ名 : visual studio 2008
投稿初めてで、わかりやすく書けているか分かりませんが、よろしくおねがいします。
Re: サイズの違うマップをステージが変わるごとにメモリ確保したい
Posted: 2011年3月15日(火) 14:11
by へろりくしょん
この場合
ReadFile( hFile , &mapdata[j] , size_w * size_h , &errchk , NULL );
は
ReadFile( hFile , &mapdata[j] , sizeof(BYTE), &errchk , NULL );
ではありませんか。
それよりも
if( mapdata == NULL )
は
mapdata = (BYTE*)malloc(sizeof(BYTE) * size_h );
の前に行う必要があると思いますよ。
Re: サイズの違うマップをステージが変わるごとにメモリ確保したい
Posted: 2011年3月15日(火) 15:02
by non
65行目のif文は何をしたいのでしょうか?
いらないと思います。っていうより、あるのはまずい。
それとも、横1列ずつ読み込みたいのか・・・
ついでに、60行のprintfも、何をしたい?
書くなら64行目でしょう。
Re: サイズの違うマップをステージが変わるごとにメモリ確保したい
Posted: 2011年3月15日(火) 18:29
by 紅いオレンジ色
返信が遅れて申し訳ありません。
まず、へろり様に対しての返信になりますが
ReadFile( hFile , &mapdata[j] , size_w * size_h , &errchk , NULL );
↓
ReadFile( hFile , &mapdata[j] , sizeof(BYTE), &errchk , NULL );
という、ご指摘をしていただきましたが、ReadFileの第3引数は読み取り対象のバイト数とあったのですが、読み取るサイズの大きさだけではなく、BYTE型としてバイト数を指定する必要があるということでしょうか?
Re: サイズの違うマップをステージが変わるごとにメモリ確保したい
Posted: 2011年3月15日(火) 18:41
by へろりくしょん
紅いオレンジ色 さんが書きました:という、ご指摘をしていただきましたが、ReadFileの第3引数は読み取り対象のバイト数とあったのですが、読み取るサイズの大きさだけではなく、BYTE型としてバイト数を指定する必要があるということでしょうか?
コード:
for( i=0; i<size_w; i++ ){
for( j=0; j<size_h; j++ ){
ReadFile( hFile , &mapdata[i][j] , size_w * size_h , &errchk , NULL );
}
}
というループで、変数 mapdata
[j] の全要素に大して1つ1つ読み込んでいる訳ですから、指定するサイズは mapdata の1要素のサイズです。
この変数 mapdata[][] の型は BYTE ですから、sizeof(BYTE) とするべきでは。 という事です。
列単位・行単位で読み込む場合は次のようになります。
コード:
for( i=0; i<size_w; i++ ){
ReadFile( hFile , &mapdata[i], sizeof(BYTE) * size_h , &errchk , NULL );
}
今回のコード
ReadFile( hFile , &mapdata
[j] , size_w * size_h , &errchk , NULL );
では、全要素を一気に読み込みにかかる事になりますね。
この場合は、ループは必要ありません。
しかし、mapdata の型は BYTE** 型で、mapdata[] の型は BYTE* 型です。
mapdata は、2次元配列ではなく、あくまでもポインタの配列となることに注意してください。
ですので、
ReadFile( hFile , &mapdata[j] , size_w * size_h , &errchk , NULL );
では、もれなくバッファオーバーランを引き起こします。
#アホみたいなタイポがありましたので、修正しました。
Re: サイズの違うマップをステージが変わるごとにメモリ確保したい
Posted: 2011年3月15日(火) 18:47
by 紅いオレンジ色
返信遅れて申し訳ありません。
non様に対しての返信になりますが、
65行目のif文ですが、
↓にきたらbreakして縦を1つうごいて、また横に読み込んでいくと、言う風に考えていました。(■はマップチップ
■■■■■■■■■■■■■■■
■■■■■■■■■■■■■■■
■■■■■■■■■■■■■■■
ですが、確かに良く考えるとまったく意味がないですね (゚ー゚;Aアセアセ
60行目のprintfはデバック用に、mapdata[j]の中身を見ようと思ってました。
Re: サイズの違うマップをステージが変わるごとにメモリ確保したい
Posted: 2011年3月15日(火) 19:55
by 紅いオレンジ色
へろり様、素早い返信ありがとうございます。
非常に分かりやすい解説ありがとうございます、どうやら自分は二次元配列と、ポインタのポインタを混同して考えていたようですね。
他サイト内等で、mapdata[0]
などでループで回しているいるのはそうゆうことだったんですね。納得いたしました。
後、さらに質問となるのですが、マップのチップ番号を返す関数を作りたいのですが
コード:
//Map.cpp内
//チップ番号を返す関数
int GetHitChip( int x , int y ){
int map_x = x / CHIP_WIDTH;
int map_y = y / CHIP_HEIGHT;
return mapdata[map_y][map_x]; ←デバックするとここで、エラーが発生してしまいます。( ハンドルされていない例外が発生しました~読み込み中にアクセス違反が発生しました。)
}
GetHitChip関数内の引数には自機キャラのx,y座標を指定し、マップチップに当たっているかどうかを調べたいと思っています。
コード:
if( GetHitChip( g_player.ch.x + g_player.ch.mx , g_player.ch.y + g_player.ch.my ) > /*地面に値するチップ番号*/ ||
GetHitChip( g_player.ch.x + g_player.ch.mx , g_player.ch.y + 16 + g_player.ch.my ) >/*上記と同じ*/ ||
GetHitChip( g_player.ch.x + g_player.ch.mx , g_player.ch.y + 23 + g_player.ch.my ) >/*上記と同じ*/ ||
GetHitChip( g_player.ch.x + g_player.ch.mx , g_player.ch.y + g_player.ch.h + g_player.ch.my ) > /*上記と同じ*/ ){
//地面と接していた時の処理
}
といった、感じにしたいのですがメモリも確保しているのに、なぜアクセス違反になってしまうんでしょうか?
Re: サイズの違うマップをステージが変わるごとにメモリ確保したい
Posted: 2011年3月16日(水) 09:56
by へろりくしょん
紅いオレンジ色 さんが書きました:といった、感じにしたいのですがメモリも確保しているのに、なぜアクセス違反になってしまうんでしょうか?
GetHitChip() 関数をコールしている if() 文の中身についてはさっぱり分かりませんので、ざっくり無視しますが、
GetHitChip() 関数自体のコードには、特に問題は見受けられません。
アクセス違反と言う事ですから、変数 mapdata のメモリ確保に失敗しているか、map_x map_y が異常値でバッファオーバーランを起こしている。 のどちらかである可能性が高いですね。
こういう、プログラムが正しく動作していれば異常値になる事などあり得ないという状況下では、とりあえず assert() を仕掛けて見るのが一番です。
ところで、MapLoad() 関数で、mapdata を解放しているようですが。
GetHitChip() 関数内で利用されている mapdata はダングリングポインタだといった事はありませんか?
Re: サイズの違うマップをステージが変わるごとにメモリ確保したい
Posted: 2011年3月16日(水) 23:44
by 紅いオレンジ色
へろり様に返信になります。
前回から続いて返信が遅れて申し訳ありません。
自分に「assert()関数」についての知識が無く、持っている参考書にも記載が無かった為インターネットを漁っておりました。
ご指摘いただいたよう以下のように追加してみました。
コード:
//指定の座標のチップ番号を返す
int GetHitChip( int x , int y ){
int map_x = x / CHIP_WIDTH; //CHIP_WIDTH = 32 マクロをヘッダーファイルで設定しています
int map_y = y / CHIP_HEIGHT; //CHIP_HEIGHT = 32 マクロをヘッダーファイルで設定しています
assert( 0 <= map_x && map_x <= 5 );//マップチップの種類が5種類のため0~5
assert( 0 <= map_y && map_y <= 5 );
//番号を返す
return mapdata[map_y][map_x];
}
ですが、assert()関数の行にブレークポイントをしてデバックを実行してみましたが、assert()関数でエラーになることなく、結果は同じでした。><
後、ダングリングポインタについてですが、こちらも聞いた事のない単語でしたので調べてみましたが、「セキュリティーの惰弱性」と出ました。
なんらかの原因の為、メモリの割り当てが解除されてしまいそこを参照してしまい予測出来ない動作が発生してしまった、というふうに載っていました。
ここまでは分かったのですが、なぜ、メモリが解放されてしまったのか(又は有効なオブジェクトが保持していないのか)が分かりません。
後、前回説明不足でしたGetHItChip()関数ですが
コード:
if( GetHitChip( g_player.ch.x + g_player.ch.mx , g_player.ch.y + g_player.ch.my ) > /*地面に値するチップ番号*/ ||
GetHitChip( g_player.ch.x + g_player.ch.mx , g_player.ch.y + 16 + g_player.ch.my ) >/*上記と同じ*/ ||
GetHitChip( g_player.ch.x + g_player.ch.mx , g_player.ch.y + 23 + g_player.ch.my ) >/*上記と同じ*/ ||
GetHitChip( g_player.ch.x + g_player.ch.mx , g_player.ch.y + g_player.ch.h + g_player.ch.my ) > /*上記と同じ*/ ){
//チップと接していた時の処理
}
GetHitChip関数で返したチップ番号にキャラクターが接触しているかを調べています。
このif文でキャラクターの左側の一番上・上から16ドットした・23ドット下・一番下の4箇所とチップの側面の当たり判定をしています。
説明下手で申し訳ありません。
Re: サイズの違うマップをステージが変わるごとにメモリ確保したい
Posted: 2011年3月17日(木) 00:41
by h2so5
GetHitChip関数はどのタイミングで呼んでいますか?
MapLoad関数の後に呼んでいるなら当然エラーになりますよ。
その時点ですでにMapLoad関数内で mapdata は解放されています。
Re: サイズの違うマップをステージが変わるごとにメモリ確保したい
Posted: 2011年3月17日(木) 01:05
by 紅いオレンジ色
h2so5様 ご指摘ありがとうございます。
このGetHitChip()関数は、プレイヤーとチップの当たり判定で、player.cpp内に記述しゲーム処理をまとめている、Game.cpp内で呼んでいます。
コード:
//Game.cpp内
void GamePlay(){
PLAYER *lp = NULL;
BOSSWEAPON *lBs = NULL;
for( int i=0; i<BOSS_WEAPON_MAX; i++ ){
lBs = GetBossWeapon(i);
}
lp = GetPlayer();
/*----プレイ中メイン処理----*/
PlayerMain(); //プレイヤー処理 //←ここでGetHitChip(プレイヤー用)関数を呼び出してます
EnemyMain(); //敵処理
PlayerWeaponMain(); //プレイヤー処理
BossWeaponMain(); //ボス武器処理
BossMain(); //ボス処理
ItemMain(); //アイテム処理
TrapMain(); //トラップ処理
HitCheckMain(); //当り判定処理
TopMenuMain(); //メニュー処理
MapMain(); //マップ処理 //←ここでMapLoad関数の呼び出しを行っています
}
/*----描画----*/
//背景1
//DD_BitBlt( g_lpDDSBack , 0 , 0 , 640 , 480 , g_lpBmp[3]->lpDDOffScreen , 0 , 0 , DDBLTFAST_WAIT | DDBLTFAST_SRCCOLORKEY );
MapDraw(backWnd); //マップ表示
TrapDraw(backWnd); //トラップ表示
PlayerDraw(backWnd); //プレイヤー表示
EnemyDraw(backWnd); //敵表示
BossDraw(backWnd); //BOSS表示
PlayerWeaponDraw(backWnd); //爆弾表示
BossWeaponDraw(backWnd); //ボス武器表示
ItemDraw(backWnd); //アイテム標示
TopMenuDraw(backWnd); //上部メニュー
}
といった感じになっています
Re: サイズの違うマップをステージが変わるごとにメモリ確保したい
Posted: 2011年3月17日(木) 01:08
by h2so5
MapLoad関数が呼ばれる前にGetHitChip関数を呼んでも、
mapdata は空なのでエラーになりますよ。
MapLoad関数内でmapdataを解放しないでおき、
その後にGetHitChip関数呼ぶ必要があります。
Re: サイズの違うマップをステージが変わるごとにメモリ確保したい
Posted: 2011年3月17日(木) 01:27
by 紅いオレンジ色
h2so5様さっそくの返信ありがとうございます。
ご指摘のように、以下のように変更しました。
コード:
//Game.cpp内
MapMain(); //マップ処理 //←一番上にMapLoad関数が含まれているMapMain関数を持ってきました。
PlayerMain(); //プレイヤー処理
EnemyMain(); //敵処理
PlayerWeaponMain(); //プレイヤー処理
BossWeaponMain(); //ボス武器処理
BossMain(); //ボス処理
ItemMain(); //アイテム処理
TrapMain(); //トラップ処理
HitCheckMain(); //当り判定処理
TopMenuMain();
後、free()関数をコメント化して、実行してみましたが、これでもだめなようです・・・・><いったいどこでおかしくなっているのやら・・・
h2so5様お手数なのですが、MapLoad関数内が正しくメモリ確保できていて、ReadFile関数が正しく出来ているか見てもらえないでしょうか。
コード:
//マップロード用関数(引数 1.ファイル名 2.読み込むデータ 3.読み込むデータのサイズ)
BOOL MapLoad( char *test , BYTE **mapdata , int size_w , int size_h ){
HANDLE hFile = NULL;//ファイルハンドルの格納用
DWORD errchk;//エラーチェック用
int i=0 , j=0;
hFile = CreateFile(
test, //読み込むファイルパス
GENERIC_READ, //読込みモード
FILE_SHARE_READ, //他のファイルからの読込みを許可
NULL,
OPEN_EXISTING, //既存のファイルを開く(無ければエラー)
FILE_ATTRIBUTE_NORMAL, //通常属性
NULL );
//開けたかどうかを確認
if( hFile == INVALID_HANDLE_VALUE ){
return FALSE; //オープンに失敗していたら終了
}
printf("マップデータ読み込んだ\n");
if( mapdata == NULL ){
printf( "メモリの確保に失敗!!\n" );
exit(-1);
}else{
printf( "メモリの確保に成功!!\n" );
}
mapdata = (BYTE**)malloc(sizeof(BYTE*) * size_w );
for( i=0; i<size_w; i++ ){
mapdata[i] = (BYTE*)malloc(sizeof(BYTE) * size_h );
}
//読み込みを行う
for( i=0; i<size_w; i++ ){
ReadFile( hFile , &mapdata[i] , sizeof(BYTE)*(size_h*size_w) , &errchk , NULL );
}
//ファイルポインタの位置を設定
//SetFilePointer( hFile, 0, NULL, FILE_BEGIN );
CloseHandle( hFile ); //ファイルを閉じる
for( int k=0; k<size_w; k++ ){
free( * ( mapdata + k ) );
}
free( mapdata );
return TRUE;
}
MapLoad関数は以上のようになっています、なにとぞよろしくお願いします。
Re: サイズの違うマップをステージが変わるごとにメモリ確保したい
Posted: 2011年3月17日(木) 01:48
by h2so5
このソースだと、100%「メモリの確保に失敗!!」と表示されると思います
コード:
if( mapdata == NULL ){
printf( "メモリの確保に失敗!!\n" );
exit(-1);
}else{
printf( "メモリの確保に成功!!\n" );
}
mapdata = (BYTE**)malloc(sizeof(BYTE*) * size_w );
メモリを確保する前にメモリのチェックをしていますよ。