構造体の二次元配列を動的に確保する方法について

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

構造体の二次元配列を動的に確保する方法について

#1

投稿記事 by ココ » 10年前

再び失礼します。
構造体の二次元目のみ動的に確保したいのですが、どの様に書けば宜しいでしょうか?

コード:

//ユーザーの構造体
typedef struct{
	DATA test_data[2];
}USER_DATA;

static USER_DATA *user_data[2];

void test{
             //データがあればロードを行う
             errno_t error_flag;
	//オプション設定の読み込み
	FILE *file_p;

      int count = 0;

             /* ここでcountにデータから数値を読み込んでます */
	
	//試合データの読み込み
	char pass[64] = {0};
	sprintf_s(pass, 10, "user_data/data%d.dat",i+1);
	if( (error_flag = fopen_s(&file_p,pass,"rb")) != 0 ){
	}else{
	             //動的に要素数を確保する(事前に読み込んだcount分の要素数を確保した二次元配列構造体を作成したい)
		user_data[i] = new USER_DATA[i][count];
					
		fread( &user_data[i], sizeof(USER_DATA[2]), 1, file_p);

		fclose(file_p);
	}
}
だいぶ省略や改変をしておりますが、これでは動的確保で配列のバウンドが定数式ではありません。とエラーが発生してしまいます。
どの様に書けば宜しいでしょうか?

またそれとは関係が無いのですが、下記画像のようにコードが改行されない状態になってしまっているのですが解決の方法はありますでしょうか・・・?
http://iup.2ch-library.com/i/i1092249-1387464579.png

アバター
みけCAT
記事: 6734
登録日時: 13年前
住所: 千葉県
連絡を取る:

Re: 構造体の二次元配列を動的に確保する方法について

#2

投稿記事 by みけCAT » 10年前

iをどこで変化させているかわからないですが、素直に

コード:

user_data[i] = new USER_DATA[count];
ではダメなのですか?
ココ さんが書きました:またそれとは関係が無いのですが、下記画像のようにコードが改行されない状態になってしまっているのですが解決の方法はありますでしょうか・・・?
http://iup.2ch-library.com/i/i1092249-1387464579.png
IE10以外のブラウザを使ってください。
複雑な問題?マシンの性能を上げてOpenMPで殴ればいい!(死亡フラグ)

ココ

Re: 構造体の二次元配列を動的に確保する方法について

#3

投稿記事 by ココ » 10年前

すみません、だいぶおかしな事を書いているみたいなので他のブラウザを用意する様努めます。
用意するのに時間が掛かるかもしれないため、いったん解決とさせて頂きます。
またみけCATさんの方法を試してみます。

有り難う御座いますm(_ _)m

ココ

Re: 構造体の二次元配列を動的に確保する方法について

#4

投稿記事 by ココ » 10年前

コード:

//ユーザーの構造体
typedef struct{
    DATA test_data[2];
}USER_DATA;
 
static USER_DATA *user_data[2];

static int count;
 
void Init{
    //データがあればロードを行う
    errno_t error_flag;
    FILE *file_p;
 
  int count = 0;
 
     /* ここでcountにデータから数値を読み込んでます */
    
    //データの読み込み
    char pass[64] = {0};
    sprintf_s(pass, 64, "user_data/data%d.dat",1);
    if( (error_flag = fopen_s(&file_p,pass,"rb")) != 0 ){
    }else{
        //動的に要素数を確保する(事前に読み込んだcount+1分の要素数を確保した二次元配列構造体を作成したい)
        user_data[0] = new USER_DATA[count+1];
        if(user_data[0] == NULL){
		//エラー処理(エラーメッセージを表示させています)
		return;
	}
                    
    fread( &user_data[0], sizeof(USER_DATA[2]), 1, file_p);
 
    fclose(file_p);
}
void Save{
    //データの読み込み
    char pass[64] = {0};
    sprintf_s(pass, 64, "user_data/data%d.dat",1);

    //データ数の増加
    count++;

    //データを構造体へ保存
  user_data[0][count-1].test_data = 入力されたデータの構造体

    errno_t error_flag;
    FILE *file_p;
    char pass[64] = {0};
    sprintf_s(pass, 64, "user_data/data%d.dat",1);
    if( (error_flag = fopen_s(&file_p,pass,"wb")) != 0 ){
    }else{
        //動的に要素数を確保する(データはどんどん増えていくのでcount+1の要素を確保し続ける)
        user_data[0] = new USER_DATA[count+1];
        if(user_data[0] == NULL){
		//エラー処理
		return;
	}
                    
        fwrite( &user_data[0], sizeof(USER_DATA[2]), 1, file_p);
 
        fclose(file_p);
    }
}
void Delete{
        for(int i=0; i<2; i++){
		//もしメモリ確保が行われていたら解放
		if(user_data[i] != NULL){
			delete[] user_data[i];
			user_data[i] = NULL;
		}
	}
}
みけCATさんのご指摘頂いた通りに改善致しまして、現在このように設計しております。
Deleteはプログラム終了直前に一度だけ呼び出します。

この状態において、一度Save関数が呼び出された後に終了する場合は正常終了するのですが、
Saveを通らずに終了させるとDeleteにてハンドルされていない例外アクセスエラーが発生してしまいます。

Initでうまく確保ができていないのかと、デバッグポイントを設定して調べましたが、エラー処理の部分は正常にスルーされていました。

ポインタ構造体と動的なメモリの確保、解放は経験が無かったために色々とまずい実装をしているんだろうと思いますが、ご指摘頂ければ助かりますm(_ _)m

ココ

Re: 構造体の二次元配列を動的に確保する方法について

#5

投稿記事 by ココ » 10年前

[追加]
この処理を実装し、保存→終了を行った後に再起動をした所
ユーザーが入力を行う構造体に、前回の処理で保存された構造体の値が入ってしまっています。
(そのような代入処理は一切行っておりません)
これはSave関数においてポインタ構造体がユーザー構造体の情報を受け取る際に、値ではなく構造体のアドレスを受け取ってしまっているからでしょうか?

しかしそれでは再起動後ユーザー構造体は初期化されてしまっているため、同じアドレスを指していたとしても値は初期化されているはずなので
かなり困惑しております・・・。

一体どういう事なのでしょうか。。

アバター
みけCAT
記事: 6734
登録日時: 13年前
住所: 千葉県
連絡を取る:

Re: 構造体の二次元配列を動的に確保する方法について

#6

投稿記事 by みけCAT » 10年前

かなり不可解な現象が発生していますね。
実際に実行してテストしたいのですが、
(著作権などの関係で)コード全体を提示することは難しいですか?
動作検証ができる最小のコード、または最小まで行かなくても小さめのコードでかまいません。
複雑な問題?マシンの性能を上げてOpenMPで殴ればいい!(死亡フラグ)

ココ

Re: 構造体の二次元配列を動的に確保する方法について

#7

投稿記事 by ココ » 10年前

著作権的には問題無いのですが、実行部分だけを取り除くのが難しく・・・。
ただ上記したコードにメインループを作りキーが押されたらSaveを呼び出すなどの形で実装出来そうなので試してみます。

他には何か一見してまずい部分は無いでしょうか?
ポインタ構造体がアドレスを指していないなど。

あれからずっと改変しているのですが、宣言の所を変更し

static USER_DATA *user_data_p[2];
static USER_DATA user_data[2];

としてポインタ構造体が使用される箇所で

user_data_p = &user_data;

とアドレスを指定する事により、メモリ解放に関してエラーは出なくなりました。
しかしユーザー入力関数を受け取る箇所で

user_data[0][count-1].test_data = 入力されたデータの構造体

これでは二次元目の部分([count-1]の箇所)が宣言で存在していないため、エラーが。
元々の方法ではエラーは出ませんが、ポインタ構造体に保存していたため値ではなく恐らくアドレスが入ってしまっているのではないかなと邪推しております。

また本来であれば
user_data_p[0][count-1]->test_data = 入力データ
とアロー演算子を使用すればポインタ構造体の値にもアクセス出来るはずなのですが、
クラス 'USER_DATA' にはオーバーロードされたメンバ 'operator ->' がありません。
とエラーが出てしまいます・・・。

ただいまから外出のため返答が遅れてしまいますが、よろしくお願い致しますm(_ _)m

アバター
usao
記事: 1887
登録日時: 11年前

Re: 構造体の二次元配列を動的に確保する方法について

#8

投稿記事 by usao » 10年前

>だいぶ省略や改変をしておりますが、
これ↑の程度がすさまじすぎて,
もはや貼られているコードらしきものに何の信ぴょう性も無いように見えるのですが,
(これってC言語なんですよね?きっと.)

とりあえず 真のコードでも
fread( &user_data[0], sizeof(USER_DATA[2]), 1, file_p);
のように書いているのでしょうか?
読み込んだデータを保存する場所として指定されている &user_data[0] って一体どこなんでしょう.

あと,文面から察するに Init → Save の順に処理しているっぽいのですが
それだとnewが2回あって,どちらもuser_data[0]に受けているようにも見えるけど,そこらへんのところとかは大丈夫なんでしょうか?


>static USER_DATA *user_data_p[2];
>static USER_DATA user_data[2];
>としてポインタ構造体が使用される箇所で
>user_data_p = &user_data;
>とアドレスを指定する事により、メモリ解放に関してエラーは出なくなりました。

これだともはや動的確保とか解放とかいう話じゃなくなってると思うのですが…?

>user_data_p[0][count-1]->test_data = 入力データ
user_data_pはどんな型でしょうか?
user_data_p[0] はどんな型でしょうか?
user_data_p[0][0]はどんな型でしょうか?
…という事柄を確認していけばわかるのではないでしょうか.
(↑のfreadの件もそうですが,何かこの辺でちょっとした勘違いをされているだけのような?)

ココ

Re: 構造体の二次元配列を動的に確保する方法について

#9

投稿記事 by ココ » 10年前

>>usaoさん
C言語のためmallocを扱うべきなのですが、newの方が安全という情報を多く目にしたためnewに切替えて試行錯誤しております。
コードに関しては過去に実行出来る状態で投稿させて頂いたのですが、質問外の箇所について手厳しいご意見を多く頂いてからトラウマが出来てしまい、見せるのをためらってしまってます・・・すみません・・・。

ポインタ構造体であるのに&でアドレスを見に行く必要はありませんでしたorz
fread,fwriteから&を取り除き
fread( user_data[0], sizeof(USER_DATA[2]), 1, file_p);
としました。

Saveで確保を行う前に解放を行っておりませんでした・・・。

おっしゃるようにかなり本末転倒というか迷走を始めてしまっていたので、元に戻し、現在

コード:

//ユーザーの構造体
typedef struct{
    DATA test_data[2];
}USER_DATA;
 
static USER_DATA *user_data[2];

//ユーザー入力構造体
typedef struct{
    int test;
}DATA;

static DATA input_user_data;
 
static int count[2];
 
void Init(){
    //データがあればロードを行う
    errno_t error_flag;
    FILE *file_p;
 
  int count = 0;
 
     for( int i=0; i<2; i++){
        /* ここでcountにデータから数値を読み込んでます */
        //もしcountが0以外=データが存在していれば読み込む
        if(count[i] > 0){    
            //データの読み込み
            char pass[64] = {0};
            sprintf_s(pass, 64, "user_data/data%d.dat",i+1);
            if( (error_flag = fopen_s(&file_p,pass,"rb")) != 0 ){
            }else{
                //動的に要素数を確保する(事前に読み込んだcount+1分の要素数を確保した二次元配列構造体を作成したい)
                user_data[i] = new USER_DATA[count[i]+1];
                if(user_data[i] == NULL){
                      //エラー処理(エラーメッセージを表示させています)
                       return;
                }
                    
                 fread( user_data[i], sizeof(USER_DATA[2]), 1, file_p);
 
                 fclose(file_p);
             }
        }
    }
}
void Save( int i){
    //データの読み込み
    char pass[64] = {0};
    sprintf_s(pass, 64, "user_data/data%d.dat",i+1);
 
    //データ数の増加
    count[i]++;

    //動的に確保する前に解放を行う
    delete[] user_data[i];
    user_data[i] = NULL;

    //動的に要素数を確保する(データはどんどん増えていくのでcount+1の要素を確保し続ける)
   user_data[i] = new USER_DATA[count[i]+1];
 
    //データを構造体へ保存
  user_data[i][count[i]-1].test_data = input_test_data;
 
    errno_t error_flag;
    FILE *file_p;
    char pass[64] = {0};
    sprintf_s(pass, 64, "user_data/data%d.dat",i+1);
    if( (error_flag = fopen_s(&file_p,pass,"wb")) != 0 ){
    }else{
        if(user_data[i] == NULL){
            //エラー処理
            return;
        }
                    
        fwrite( user_data[i], sizeof(USER_DATA[2]), 1, file_p);
 
        fclose(file_p);
    }
}
void Delete(){
        for(int i=0; i<2; i++){
        //もしメモリ確保が行われていたら解放
        if(user_data[i] != NULL){
            delete[] user_data[i];
            user_data[i] = NULL;
        }
    }
}
という形で色々形を直してみましたが如何でしょうか・・・?
様々なバグは解決しましたが、一度保存処理を抜けた後に再起動するとInitのnew処理で
ハンドルされていない例外が発生しました: Microsoft C++ の例外: std::bad_alloc
というエラーが発生しており、現在原因を究明しております・・・・・orz

ISLe
記事: 2650
登録日時: 13年前
連絡を取る:

Re: 構造体の二次元配列を動的に確保する方法について

#10

投稿記事 by ISLe » 10年前

根本的な問題として、添字が要素番号を表す箇所と要素数を表す箇所の区別ができていないのでは。
+1とか-1とか行き当たりばったりなのが見るからに明らかで添字の変化に頭が追い付いていないと思います。

とりあえず、メモリ確保もファイルの読み書きも後回しにして、添字とかサイズとか使用する数値の確認作業を行うべきかと。

意味もなくnewを使うくらいならvector使ったほうが良い気がします。

アバター
みけCAT
記事: 6734
登録日時: 13年前
住所: 千葉県
連絡を取る:

Re: 構造体の二次元配列を動的に確保する方法について

#11

投稿記事 by みけCAT » 10年前

せめてコンパイルが通るコードが欲しかったです。
とりあえずコンパイルして実行ファイルが生成できるところまで修正しました。

コード:

#include <cstdio>

void sprintf_s(char* a,int dummy,const char* puttern,int num) {
	sprintf(a,puttern,num);
}
typedef int errno_t;
errno_t fopen_s(FILE** pfp,const char* fileName,const char* flags) {
	*pfp=fopen(fileName,flags);
	return *pfp==NULL;
}

//ユーザー入力構造体
typedef struct{
	int test;
}DATA;

//ユーザーの構造体
typedef struct{
	DATA test_data[2];
}USER_DATA;

static USER_DATA *user_data[2];

static DATA input_user_data;

static DATA input_test_data;

static int count[2];

void Init(){
	//データがあればロードを行う
	errno_t error_flag;
	FILE *file_p;

#if 0
	int count = 0;
#else
	int count[2]={0,0};
#endif

	for( int i=0; i<2; i++){
		/* ここでcountにデータから数値を読み込んでます */
		//もしcountが0以外=データが存在していれば読み込む
		if(count[i] > 0){    
			//データの読み込み
			char pass[64] = {0};
			sprintf_s(pass, 64, "user_data/data%d.dat",i+1);
			if( (error_flag = fopen_s(&file_p,pass,"rb")) != 0 ){
			}else{
				//動的に要素数を確保する(事前に読み込んだcount+1分の要素数を確保した二次元配列構造体を作成したい)
				user_data[i] = new USER_DATA[count[i]+1];
				if(user_data[i] == NULL){
					//エラー処理(エラーメッセージを表示させています)
					return;
				}

				fread( user_data[i], sizeof(USER_DATA[2]), 1, file_p);

				fclose(file_p);
			}
		}
	}
}
void Save( int i){
	//データの読み込み
	char pass[64] = {0};
	sprintf_s(pass, 64, "user_data/data%d.dat",i+1);

	//データ数の増加
	count[i]++;

	//動的に確保する前に解放を行う
	delete[] user_data[i];
	user_data[i] = NULL;

	//動的に要素数を確保する(データはどんどん増えていくのでcount+1の要素を確保し続ける)
	user_data[i] = new USER_DATA[count[i]+1];

	//データを構造体へ保存
#if 0
	user_data[i][count[i]-1].test_data = input_test_data;
#endif

	errno_t error_flag;
	FILE *file_p;
#if 0
	char pass[64] = {0};
#endif
	sprintf_s(pass, 64, "user_data/data%d.dat",i+1);
	if( (error_flag = fopen_s(&file_p,pass,"wb")) != 0 ){
	}else{
		if(user_data[i] == NULL){
			//エラー処理
			return;
		}

		fwrite( user_data[i], sizeof(USER_DATA[2]), 1, file_p);

		fclose(file_p);
	}
}
void Delete(){
	for(int i=0; i<2; i++){
		//もしメモリ確保が行われていたら解放
		if(user_data[i] != NULL){
			delete[] user_data[i];
			user_data[i] = NULL;
		}
	}
}

int main(void) {
	return 0;
}
複雑な問題?マシンの性能を上げてOpenMPで殴ればいい!(死亡フラグ)

アバター
みけCAT
記事: 6734
登録日時: 13年前
住所: 千葉県
連絡を取る:

Re: 構造体の二次元配列を動的に確保する方法について

#12

投稿記事 by みけCAT » 10年前

さらに、問題点をコメントとして書き込み、最低限テストできるようにしました。

コード:

// ここから追加
#include <cstdio>

void sprintf_s(char* a,int dummy,const char* puttern,int num) {
	sprintf(a,puttern,num);
}
typedef int errno_t;
errno_t fopen_s(FILE** pfp,const char* fileName,const char* flags) {
	*pfp=fopen(fileName,flags);
	return *pfp==NULL;
}
// ここまで追加

// 位置を移動
//ユーザー入力構造体
typedef struct{
	int test;
}DATA;

//ユーザーの構造体
typedef struct{
	DATA test_data[2];
}USER_DATA;

static USER_DATA *user_data[2];

static DATA input_user_data;

static DATA input_test_data; // 追加

static int count[2];

void Init(){
	//データがあればロードを行う
	errno_t error_flag;
	FILE *file_p;

	// 警告 : グローバル変数と名前が被っています(-Wshadow)
#if 0
	// エラー : int型の変数に対し[]を使うことはできないはず
	int count = 0;
#else
	int count[2]={1,1};
#endif

	for( int i=0; i<2; i++){
		/* ここでcountにデータから数値を読み込んでます */
		//もしcountが0以外=データが存在していれば読み込む
		if(count[i] > 0){
			//データの読み込み
			char pass[64] = {0};
			sprintf_s(pass, 64, "user_data/data%d.dat",i+1);
			if( (error_flag = fopen_s(&file_p,pass,"rb")) != 0 ){
			}else{
				//動的に要素数を確保する(事前に読み込んだcount+1分の要素数を確保した二次元配列構造体を作成したい)
				user_data[i] = new USER_DATA[count[i]+1];
				if(user_data[i] == NULL){
					//エラー処理(エラーメッセージを表示させています)
					return;
				}

				// 警告 : せっかく動的に要素数を確保しても、ここで読み込む数が固定なのはおかしい
				fread( user_data[i], sizeof(USER_DATA[2]), 1, file_p);

				fclose(file_p);
			}
		}
	}
}
void Save( int i){
	//データの読み込み
	char pass[64] = {0};
	sprintf_s(pass, 64, "user_data/data%d.dat",i+1);

	//データ数の増加
	count[i]++;

	//動的に確保する前に解放を行う
	delete[] user_data[i];
	user_data[i] = NULL;

	//動的に要素数を確保する(データはどんどん増えていくのでcount+1の要素を確保し続ける)
	user_data[i] = new USER_DATA[count[i]+1];

	//データを構造体へ保存
#if 0
	// エラー : 未定義の識別子
	// エラー : 普通の配列に対して直接代入することは、標準ではできないはず
	user_data[i][count[i]-1].test_data = input_test_data;
#else
	user_data[i][count[i]-1].test_data[0] = input_test_data;
	user_data[i][count[i]-1].test_data[1] = input_test_data;
#endif

	errno_t error_flag;
	FILE *file_p;
#if 0
	// エラー : 二重定義
	char pass[64] = {0};
#endif
	// 警告 : 上と処理が被っている
	sprintf_s(pass, 64, "user_data/data%d.dat",i+1);
	if( (error_flag = fopen_s(&file_p,pass,"wb")) != 0 ){
	}else{
		if(user_data[i] == NULL){
			//エラー処理
			return;
		}

		// 警告 : ここも書き込むサイズが固定なのはあまり良くないと思う
		fwrite( user_data[i], sizeof(USER_DATA[2]), 1, file_p);

		fclose(file_p);
	}
}
void Delete(){
	for(int i=0; i<2; i++){
		//もしメモリ確保が行われていたら解放
		if(user_data[i] != NULL){
			delete[] user_data[i];
			user_data[i] = NULL;
		}
	}
}

// ここから追加
int main(void) {
	Init();
	for(int i=0;i<2;i++) {
		for(int j=0;j<2;j++) {
			if(user_data[i]) {
				printf("user_data[%d][%d] : %d %d\n",
					i,j,user_data[i][j].test_data[0].test,user_data[i][j].test_data[1].test);
			}
		}
	}
	scanf("%d",&input_test_data.test);
	Save(0);
	Save(1);
	Delete();
	return 0;
}
// ここまで追加
複雑な問題?マシンの性能を上げてOpenMPで殴ればいい!(死亡フラグ)

ココ

Re: 構造体の二次元配列を動的に確保する方法について

#13

投稿記事 by ココ » 10年前

>>ISLeさん、みけCATさん
ISLeさんのおっしゃる様にまだ自分は勉強不足で、立ち入るには早い分野だと判断致しました。
当面は大きな値で静的確保を行い、そちらを安定させてからポインタに移ろうかなと思います。

みけCATさん、ソースコード申し訳御座いません。こちらでコンパイル通るコードを書こうとしていたのですがエラーが多く・・・難儀しておりました。。
提示して頂いたコードはいずれ勉強が済んだ時に参考にさせて頂こうと思います。
有り難う御座いました。

閉鎖

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