ページ 11

擬似二次元配列のメモリ確保について

Posted: 2015年3月01日(日) 17:16
by ゴンマサ
閲覧いただきありがとうございます。

恥ずかしながら前回の質問とあまり進歩してない内容になってしまいますが、
ご教授いただければ幸いです。

質問の内容ではC++およびDXライブラリを使用しています。
またビルドは通っています。

前回の質問では、strtokを利用した読み込みができなかったのですが、
メモリを確保する記述を分けたところ、うまく機能しました。
ありがとうございました。

以下のコードの78行目で、ハンドルされていない例外が、発生してしまいます。

rowとcolには値が代入されているので、メモリは確保できていると思います。

となるとメモリへの値の代入の仕方が問題だと思うのですが、
見当がつかなくなってしまいました。

参考にした主なサイトは以下になります。
紅音製作所
http://www5.big.or.jp/~high/VENIO/kuz/kuz_are_02.htm

C/C++プログラミング
http://www.asahi-net.or.jp/~uc3k-ymd/Le ... 02_07.html

何かヒントでもいただけたらと思います。
宜しくお願い致します。

コード:


#include "DxLib.h"
#include "stdio.h"
#include "string.h"
#include "stdlib.h"

//ウインドウの横と縦の大きさ
#define GAME_WINDOW_WIDTH 640//32*20
#define GAME_WINDOW_HEIGHT 480//32*15

#define SCRIPT_FILENAME "map.txt"//読み込むファイル
#define MAPCHIP_SIZE 32//マップチップサイズ
#define LINE_1 256//動的擬似二次元配列の実験用定数

int mapSizeY,mapSizeX;//読み取ったマップのサイズ

int make_sure_of_memori;//メモリ確保の可否

//メモリ確保のための変数
int **mat;//ダブルポインタの宣言
int row;//matのポインタ
int col;//rowの中身


int i,j;//LoadMapFile内で使用、forのカウント用、メモリの確保用
int l,k;//Mapchipdraw内で使用、forのカウント用
int pox,poy;//配列を代入するときのカウント用

//プロトタイプ宣言
void Mapchipdraw(int **,int,int);

//ファイルの読み込み
void LoadMapFile(char*Filename)
{
	char line[LINE_1];
	poy = 0;//DATAの行数 

	FILE*mapFile;
	mapFile = fopen(Filename,"r");
	while( fgets(line,LINE_1,mapFile))//MAP_SIZEを読み込むためだけのループ、
	//読み込みはこの一度で行わなければいけない
	{
		 //wordsにlineをカンマで区切って格納する
		 char*words = strtok(line,",");

		//ファイル内のキーワードを先頭からチェックし、振り分ける
		if(strcmp("MAP_SIZE",words) == 0 )
		{
			//要素数に設定する値をファイルから読み込む
			//この時点では要素数への代入までには至っていない
			mapSizeY = atoi(strtok(NULL,","));//一つ目
			mapSizeX = atoi(strtok(NULL,","));//二つ目--読み込まれていない
		}
		//配列を読み込む
		else if(strcmp("DATA",words) == 0)
		{
			//DATA列を見つけたら最初にメモリを確保する
			if(make_sure_of_memori == 0)
			{
				//要素数の代入
				row = mapSizeY;//現在15
				col = mapSizeX;//現在10

				//newを使ったメモリの動的確保
				int **mat = new int*[row];//rowというy軸行列、15のメモリ領域を確保

				for(i = 0; i < row; i++)
				{
					mat[row] = new int[col];//colというx軸行列、10のメモリ領域を確保
				}

				//二回目以降はメモリの確保を繰り返さない
				make_sure_of_memori = 1;
			}
			//x軸に列の内容をstrtokで読み込ませる
			for(pox = 0; pox < mapSizeX;pox++)
			{
			mat[poy][pox] = atoi(strtok(NULL,","));//アクセス違反発生

			//*(*(mat+poy)+pox) = atoi(strtok(NULL,","));//同じく発生
			}
			poy++;
		}
	}
//ファイルを閉じる
 fclose(mapFile);

}//LoadMapFile関数の終わり



//マップチップの描画
//この関数自体は有効
void Mapchipdraw(int **a,int b,int c)//ここで引数を使いbとcに代入する
{ 
	for(l = 0; l < sizeof(b); l++)//b=mapSizeY
    {
	    for(k = 0; k < sizeof(c);k++)//c=mapSizeX
		{
		    if(mat[l][k]==0)//ファイル内容を小分けして読み込んだときはデバッグがとまる
		    {
			    DrawBox(k*MAPCHIP_SIZE,
				        l*MAPCHIP_SIZE,
				        k*MAPCHIP_SIZE+MAPCHIP_SIZE,
			            l*MAPCHIP_SIZE+MAPCHIP_SIZE,
					    GetColor(255,255,255),
					    TRUE);
			}
		}
	}
}
		 

//メイン
int WINAPI WinMain( HINSTANCE hInstace, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow )
{
	
	//ウィンドウモードで起動
	ChangeWindowMode( TRUE );
	//画面の大きさは800 * 600
    SetGraphMode( GAME_WINDOW_WIDTH, GAME_WINDOW_HEIGHT, 16 ) ;
	//DxLib初期化
    if( DxLib_Init() == -1 )
    {
		return -1;
	}
    //ここでマップテキストの読み込みと、擬似配列への代入
	LoadMapFile(SCRIPT_FILENAME);

	while(CheckHitKey(KEY_INPUT_ESCAPE) == 0)
	{
		ClearDrawScreen();


		//マップチップの描画
		Mapchipdraw(mat,mapSizeY,mapSizeX);

		//デバッグ時の変数代入の確認
		//マップの大きさ
		DrawFormatString(0,180,GetColor(255,0,255),"mapSizeY%d",mapSizeY);
		DrawFormatString(0,200,GetColor(255,0,255),"mapSizeX%d",mapSizeX);

		DrawFormatString(0,240,GetColor(255,0,255),"メモリの確保row%d",row);
		DrawFormatString(0,260,GetColor(255,0,255),"メモリの確保col%d",col);

	    ScreenFlip();
	    WaitTimer(20);//これがないとボタンの反応が早すぎるため
	}

//ここで領域の開放をする
 for(i=0;i<row; i++)
 {
	 delete[]mat[i];
 }
 delete[]mat;
 
	DxLib_End();
	return 0;
}

読み込むテキストファイル

MAP_SIZE,15,10,

DATA,1,0,0,0,0, 0,0,0,0,0, 0,0,0,0,0, 0,0,0,0,0,
DATA,0,1,1,1,1, 1,1,1,1,1, 1,1,1,1,1, 1,1,1,1,0,
DATA,0,1,1,1,1, 1,1,1,1,1, 1,1,1,1,1, 1,1,1,1,0,
DATA,0,1,1,1,1, 1,1,1,1,1, 1,1,1,1,1, 1,1,1,1,0,
DATA,0,1,1,1,1, 1,1,1,1,1, 1,1,1,1,1, 1,1,1,1,0,

DATA,0,1,1,1,1, 1,1,1,1,1, 1,1,1,1,1, 1,1,1,1,0,
DATA,0,1,1,1,1, 1,1,1,1,1, 1,1,1,1,1, 1,1,1,1,0,
DATA,0,1,1,1,1, 1,1,1,1,1, 1,1,1,1,1, 1,1,1,1,0,
DATA,0,1,1,1,1, 1,1,1,1,1, 1,1,1,1,1, 1,1,1,1,0,
DATA,0,1,1,1,1, 1,1,1,1,1, 1,1,1,1,1, 1,1,1,1,0,

DATA,0,1,1,1,1, 1,1,1,1,1, 1,1,1,1,1, 1,1,1,1,0,
DATA,0,1,1,1,1, 1,1,1,1,1, 1,1,1,1,1, 1,1,1,1,0,
DATA,0,1,1,1,1, 1,1,1,1,1, 1,1,1,1,1, 1,1,1,1,0,
DATA,0,1,1,1,1, 1,1,1,1,1, 1,1,1,1,1, 1,1,1,1,0,
DATA,0,0,0,0,0, 0,0,0,0,0, 0,0,0,0,0, 0,0,0,0,0,

Re: 擬似二次元配列のメモリ確保について

Posted: 2015年3月01日(日) 17:33
by みけCAT
65行目ではローカル変数のmatに確保したメモリの場所を代入していて、78行目で参照しているmatは更新していないため、
78行目で参照しているmatの値は0であり、アクセス違反になるようです。
ゴンマサ さんが書きました:rowとcolには値が代入されているので、メモリは確保できていると思います。

となるとメモリへの値の代入の仕方が問題だと思うのですが、
見当がつかなくなってしまいました。
残念、その一歩手前で、せっかく確保したメモリ(の位置)を捨ててしまっているのが問題でした。
でも、(グローバルな)ポインタ変数もメモリ上にあるはずなので、間違ってはいないですね。

Re: 擬似二次元配列のメモリ確保について

Posted: 2015年3月01日(日) 19:09
by ゴンマサ
みけCAT様

お世話になっております。
前回の質問では大変お世話になりました。
今回も稚拙な内容ではありますが、宜しくお願い致します。

matの更新がされていないということは、

コード:

                //newを使ったメモリの動的確保
                int **mat = new int*[row];//rowというy軸行列、15のメモリ領域を確保
 
                for(i = 0; i < row; i++)
                {
                    mat[row] = new int[col];//colというx軸行列、10のメモリ領域を確保
                }
の中で確保できているメモリはrowとcolのみで、
これらを行う前、または後に、matにも値を与えなければならないということでしょうか。
とすると、この場合の値とはrowとcolをかけた150になるのでしょうか。



またメモリの位置を捨てているというのは
67行目の、

コード:

for(pox = 0; pox < mapSizeX;pox++)
の、Pox = 0;の部分を指しておられるのでしょうか。

Re: 擬似二次元配列のメモリ確保について

Posted: 2015年3月01日(日) 19:18
by みけCAT
ゴンマサ さんが書きました:matの更新がされていないということは、

コード:

                //newを使ったメモリの動的確保
                int **mat = new int*[row];//rowというy軸行列、15のメモリ領域を確保
 
                for(i = 0; i < row; i++)
                {
                    mat[row] = new int[col];//colというx軸行列、10のメモリ領域を確保
                }
の中で確保できているメモリはrowとcolのみで、
これらを行う前、または後に、matにも値を与えなければならないということでしょうか。
とすると、この場合の値とはrowとcolをかけた150になるのでしょうか。
いいえ。
・rowとcolのメモリはここで確保しているわけではありません。21行目と22行目で確保(しろという指示がコンパイラに)されています。
・ここでグローバル変数のmatに値を与えてください。前でも後でもありません。
・matに与える値は確保したメモリのアドレスです。150である可能性はゼロに近似できるでしょう。

ゴンマサ さんが書きました:またメモリの位置を捨てているというのは
67行目の、

コード:

for(pox = 0; pox < mapSizeX;pox++)
の、Pox = 0;の部分を指しておられるのでしょうか。
いいえ。74行目でスコープを抜けるとき、ローカル変数のmatのデータが破棄される部分を指しています。

Re: 擬似二次元配列のメモリ確保について

Posted: 2015年3月01日(日) 20:14
by ゴンマサ
ありがとうございます。

私なりにまとめさせていただきますと、

「ローカル変数のmatのデータの破棄をしている」とは、
58行目からのif文の中で、グローバル変数のmat、(20行目の**matの事)に、
64行目から70行目にかけての記述で確保したメモリのアドレスを与えていない。

ということでしょうか。


とすると、下記のような

コード:

 
if(make_sure_of_memori == 0)
            {
                //要素数の代入
                row = mapSizeY;//現在15
                col = mapSizeX;//現在10
 
                //newを使ったメモリの動的確保
                int **mat = new int*[row];//rowというy軸行列、15のメモリ領域を確保
 
                for(i = 0; i < row; i++)
                {
                    mat[row] = new int[col];//colというx軸行列、10のメモリ領域を確保
                }
 

         //////////
	           **mat = 「上記で確保したメモリのアドレス」
         //////////


                //二回目以降はメモリの確保を繰り返さない
                make_sure_of_memori = 1;
            }
という記述になるということでしょうか

Re: 擬似二次元配列のメモリ確保について

Posted: 2015年3月01日(日) 20:48
by box
解決策として手っ取り早そうなのは
ゴンマサ さんが書きました:

コード:

				//newを使ったメモリの動的確保
				int **mat = new int*[row];//rowというy軸行列、15のメモリ領域を確保
ここを

コード:

				//newを使ったメモリの動的確保
				mat = new int*[row];//rowというy軸行列、15のメモリ領域を確保
として、グローバル変数のmatを使うことではないか、と思います。
もともとのコードで
ゴンマサ さんが書きました:

コード:

				//newを使ったメモリの動的確保
				int **mat = new int*[row];//rowというy軸行列、15のメモリ領域を確保
と書いてあると、matは、それを定義したブロックの中だけで有効であって、ブロックを抜けると
雲散霧消していると思います。その状態で
ゴンマサ さんが書きました:

コード:

			mat[poy][pox] = atoi(strtok(NULL,","));//アクセス違反発生
こうやるとアクセス違反が出るのは必然であるように思います。

ところで、標準ヘッダーファイルをインクルードするときは

コード:

#include <stdio.h>
のように書くのが一般的であるように思います。

コード:

#include "stdio.h"
と書いてあるのを見かけることは、私はほとんどありません。

Re: 擬似二次元配列のメモリ確保について

Posted: 2015年3月01日(日) 21:00
by ゴンマサ
Box様

ご教授ありがとうございます。

標準ヘッダーファイルのインクルードについては、
私が初めてプログラミングを始めたときに参考にしていたサイトで使われていたものをそのまま使用しています。
今のところ特に問題はなさそうなのでこのままにしているのですが、何か影響が懸念されるものなのでしょうか。

Re: 擬似二次元配列のメモリ確保について

Posted: 2015年3月01日(日) 21:02
by ゴンマサ
所要につき席をはなれるため、これ以降の返事が明日になってしまいます。
申し訳ありません。

Re: 擬似二次元配列のメモリ確保について

Posted: 2015年3月01日(日) 21:16
by みけCAT
ゴンマサ さんが書きました:「ローカル変数のmatのデータの破棄をしている」とは、
58行目からのif文の中で、グローバル変数のmat、(20行目の**matの事)に、
64行目から70行目にかけての記述で確保したメモリのアドレスを与えていない。

ということでしょうか。
「データの破棄をしている」とはずれている気がしますが、問題はそれです。
ゴンマサ さんが書きました:とすると、下記のような

コード:

 
if(make_sure_of_memori == 0)
            {
                //要素数の代入
                row = mapSizeY;//現在15
                col = mapSizeX;//現在10
 
                //newを使ったメモリの動的確保
                int **mat = new int*[row];//rowというy軸行列、15のメモリ領域を確保
 
                for(i = 0; i < row; i++)
                {
                    mat[row] = new int[col];//colというx軸行列、10のメモリ領域を確保
                }
 

         //////////
	           **mat = 「上記で確保したメモリのアドレス」
         //////////


                //二回目以降はメモリの確保を繰り返さない
                make_sure_of_memori = 1;
            }
という記述になるということでしょうか
いいえ。実際にコンパイルしてみましたか?

コード:

**mat = 「上記で確保したメモリのアドレス」
という文は、int型の領域にアドレスを格納することになり、どう考えてもおかしいです。
box さんが書きました:解決策として手っ取り早そうなのは
ゴンマサ さんが書きました:

コード:

				//newを使ったメモリの動的確保
				int **mat = new int*[row];//rowというy軸行列、15のメモリ領域を確保
ここを

コード:

				//newを使ったメモリの動的確保
				mat = new int*[row];//rowというy軸行列、15のメモリ領域を確保
として、グローバル変数のmatを使うことではないか、と思います。
そうですね。この方針でいいと思います。
ついでに、わざわざ変数row、colを用いずに、

コード:

			//DATA列を見つけたら最初にメモリを確保する
			if(make_sure_of_memori == 0)
			{
				//newを使ったメモリの動的確保
				mat = new int*[mapSizeY];//mapSizeY要素のメモリ領域を確保

				for(i = 0; i < mapSizeY; i++)
				{
					mat[i] = new int[mapSizeX];//mapSizeX要素のメモリ領域を確保
				}

				//二回目以降はメモリの確保を繰り返さない
				make_sure_of_memori = 1;
			}
とするとさらによいでしょう。

Re: 擬似二次元配列のメモリ確保について

Posted: 2015年3月02日(月) 17:30
by ゴンマサ
返信が遅くなり申し訳ありませんでした。

下記のコードにてビルドがとおり、デバッグも中断することなくできました。

コード:

			//DATA列を見つけたら最初にメモリを確保する
			if(make_sure_of_memori == 0)
			{
				//rowというy軸行列、15のメモリ領域を確保
				mat = new int *[mapSizeY];

				//colというx軸行列、10のメモリ領域を確保
				for(i = 0; i < mapSizeY; i++)
				{
					mat[i] = new int[mapSizeX];

					//配列の初期化
					for (j = 0;j < mapSizeX; j++)
					{
						mat[i][j] = 0;
					}
				}
				//二回目以降はメモリの確保を繰り返さない
				make_sure_of_memori = 1;
			}



要素数と、代入のためのカウント数をごっちゃにしていたのが原因だったようです。
まだマップチップの描画に少々難がありますが、ここで解決とさせていただきます。
皆様、閲覧頂きありがとうございました。

みけCAT様
前回の質問に続き、今回も大変お世話になりました。
また機会がありましたら、宜しくお願い致します。