ページ 11

構造体の配列を利用した場合のメモリ割り当て利用

Posted: 2015年3月10日(火) 13:25
by がーまる
C言語でメモリの割当をした場合、
printf("日付%s 名前%s スコア%d\n", s2.day, s2.name, s2.score);
の部分の割り当ての利用の仕方はどうなるのでしょうか?
p->day
じゃコンパイルが通らなかったので・・・

コード:

void kekka(void)
{
	FILE *fp;

	struct s_type *p;
	p = (struct s_type *)malloc(sizeof(struct s_type));
	if (!p) {
		printf("割り当てに失敗しました");
		exit(1);
	}

	int i,cnt;
	if ((fp = fopen("data", "rb")) == NULL) {
		printf("ファイルを開けませんでした。\n");
		exit(1);
	}


	for(i = 0; i < 100; i++){
		fread(&s2[i], sizeof(struct s_type), 1, fp);
		if( ferror(fp) ){
			printf("読み込み失敗\n");
			exit(1);
		}
		if( feof(fp) ){
			#ifdef DEBUG
				printf("EOF データ数:%d\n",i);
			#endif
			break;
		}
    }

	fclose(fp);

	#ifdef DEBUG
	{
		int i;
		for(i = 0; i < 100; i++){
			printf("Debug:日付%s 名前%s スコア%d\n", s2[i].day, s2[i].name, s2[i].score);
    	}
	}
	#endif
	
	for(cnt = 0; cnt < 10; cnt++){
		i--;
		printf("日付%s 名前%s スコア%d\n", s2[i].day, s2[i].name, s2[i].score);
		if(i == 0){
			break;
		}
	}
	printf("\n");
	free(p);
	main();
}

Re: 構造体の配列を利用した場合のメモリ割り当て利用

Posted: 2015年3月10日(火) 13:44
by がーまる
追記
構造体はこのようになっています。
struct s_type {
char day[10];
char name[10];
int score;
} s, s2[100];

Re: 構造体の配列を利用した場合のメモリ割り当て利用

Posted: 2015年3月10日(火) 14:23
by みけCAT
この場合のp(すなわち*((p)+(i)))の型はstruct s_typeなので、素直に

コード:

printf("日付%s 名前%s スコア%d\n", p[i].day, p[i].name, p[i].score);
とすればいいはずです。

Re: 構造体の配列を利用した場合のメモリ割り当て利用

Posted: 2015年3月10日(火) 14:32
by がーまる
みけCATさん
p.day
ですると、結果が文字化けしてしまい、思うとおりに表示されません。
また、s2でやっているのですが、これはとくに関係はありませんでしょうか?

Re: 構造体の配列を利用した場合のメモリ割り当て利用

Posted: 2015年3月10日(火) 14:57
by みけCAT
がーまる さんが書きました:また、s2でやっているのですが、これはとくに関係はありませんでしょうか?

「s2でやっている」というのは、具体的にどこをどう書いたということでしょうか?
pにs2が指すアドレス(の付近のアドレス)を代入していたり、s2にpが指すアドレス(の付近のアドレス)を代入していたりということがなければ、基本的にs2が指すデータとpが指すデータは関係ないです。
がーまる さんが書きました:p.day
ですると、結果が文字化けしてしまい、思うとおりに表示されません。

確保した領域の範囲外にアクセスしていないか、値を設定していない領域を出力していないかを確認してください。

Re: 構造体の配列を利用した場合のメモリ割り当て利用

Posted: 2015年3月10日(火) 15:14
by がーまる
メモリ割り当てをする前はできていたので、そのまま同じ構造体を使っているので、極端に変わることはないと思いますが、
とりあえずプログラムの全部をあげてみます。

コード:

#include <stdio.h>
#include <stdlib.h>
#include <conio.h>
#include <time.h>
#include <string.h>
//#define DEBUG

void game(void);
void kekka(void);

struct s_type {
	char day[10];
	char name[10];
	int score;
} s, s2[100];

int top = 0;

int main(void)
{
	int com;

	memset(&s    ,0x00,sizeof(s));
	memset(&s2[0],0x00,sizeof(s2));

	printf(" High & Lowゲーム\n");
	printf("  1. ゲーム開始\n");
	printf("  2. 過去の成績\n");
	printf("  3. 終了\n");

	printf("コマンドを選択してください: ");
	for( ; ; ){
		com = getche();
		printf("\n");
		if(com < 49 || com > 51) {
			printf("もう一度入力してください: ");
		} else {
			break;
		}
	}
				
		switch (com) {
			case 49:
				game();
				break;
			case 50:
				kekka();
				break;
			case 51:
				printf("\nbye");
			exit(1);
		}
	return 0;
}


void game(void)
{
	struct s_type *p;
	p = (struct s_type *)malloc(sizeof(struct s_type));
	if (!p) {
		printf("割り当てに失敗しました");
		exit(1);
	}
	
	char kaitou;
	int run, num, count, sw, com, score, i;
	FILE *fp;
	struct tm *systime;
	time_t t;
	t = time(NULL);
	systime = localtime(&t);
	
	printf("プレイヤーネームを入力してください\n");
	scanf("%s", p->name);
	printf("日付: %.2d/%.2d\n", systime->tm_mon+1, systime->tm_mday);
	sprintf(p->day, "%02d/%02d", systime->tm_mon + 1, systime->tm_mday);
	for( ; ; ){
		score = 0;
		num = 7;
		printf("「High and Low」ゲーム\n大きいと思うなら「h」小さいと思うなら「l」\n");
		for(count = 1; count < 11; ){
			run = rand() % 13 + 1;
			for (sw = 0; sw < 1; ){
				printf("\n%d回目のゲームです。\n手持ちの数より大きい?小さい?\n", count);
				kaitou = getche();
				if(num == run && (kaitou == 'h' || kaitou == 'l')){
					score = score + 3;
					printf("\n同じ数だったので!+3!\n");
					count++;
					sw++;
				} else if(kaitou == 'h'){
					if(num > run){
						score = score + 10;
						printf("\n正解!+10!\n");
					} else {
						score = score - 10;
						printf("\n不正解!-10!\n");
					}
				count++;
				sw++;
				} else if(kaitou == 'l'){
					if(num < run){
						score = score + 10;
						printf("\n正解!+10!\n");
					} else {
						score = score - 10;
						printf("\n不正解!-10!\n");
					}
				count++;
				sw++;
				} else {
					printf("\n「h」と「l」以外の文字が入力されました。もう一度入力してください。\n");
				}
			}
			num = run;
		}
		printf("\n最終スコア: %d\n", score);

		p->score = score;


		if ((fp = fopen("data.txt", "a")) == NULL) {
			printf("ファイルを開けませんでした。errno:%d\n",errno);
			exit(1);
		}
		if (fwrite(&s, sizeof(p), 1, fp) != 1){
			printf("書き込みエラー\n");
			exit(1);
		}
		
		fclose(fp);
		printf("もう一度プレイしますか?(Yes:1 No:2)\n");
		for( ; ; ){
			com = getche();
			printf("\n");
			if(com < 49 || com > 50) {
				printf("もう一度入力してください: \n");
			} else {
				break;
			}
		}
		switch (com) {
			case 50:
				main();
				break;
		}
	}
	free(p);
}


void kekka(void)
{
	FILE *fp;

	struct s_type *p;
	p = (struct s_type *)malloc(sizeof(struct s_type));
	if (!p) {
		printf("割り当てに失敗しました");
		exit(1);
	}

	int i,cnt;
	if ((fp = fopen("data", "rb")) == NULL) {
		printf("ファイルを開けませんでした。\n");
		exit(1);
	}


	for(i = 0; i < 100; i++){
		fread(&s2[i], sizeof(struct s_type), 1, fp);
		if( ferror(fp) ){
			printf("読み込み失敗\n");
			exit(1);
		}
		if( feof(fp) ){
			#ifdef DEBUG
				printf("EOF データ数:%d\n",i);
			#endif
			break;
		}
    }

	fclose(fp);

	#ifdef DEBUG
	{
		int i;
		for(i = 0; i < 100; i++){
			printf("Debug:日付%s 名前%s スコア%d\n", p[i].day, p[i].name, p[i].score);
    	}
	}
	#endif
	
	for(cnt = 0; cnt < 10; cnt++){
		i--;
		printf("日付%s 名前%s スコア%d\n", s2[i].day, p[i].name, p[i].score);
		if(i == 0){
			break;
		}
	}
	printf("\n");
	free(p);
	main();
}

Re: 構造体の配列を利用した場合のメモリ割り当て利用

Posted: 2015年3月10日(火) 15:15
by がーまる
ちなみにまだ書き込みについては不十分な書き方です。

Re: 構造体の配列を利用した場合のメモリ割り当て利用

Posted: 2015年3月10日(火) 16:07
by みけCAT
やっぱり確保した領域の範囲外にアクセスし、かつ値を設定していない領域を出力していましたね。
  • 誤って使用しないようにするため、15行目の「 s, s2[100]」を削除してください。
  • 23行目と24行目(memset)を削除してください。
  • 127行目を

    コード:

    		if (fwrite(p, sizeof(*p), 1, fp) != 1){
    としてください。
  • 158行目を

    コード:

    	p = (struct s_type *)malloc(sizeof(struct s_type) * 100);
    としてください。
  • 172行目と198行目のs2をpに変えてください。

Re: 構造体の配列を利用した場合のメモリ割り当て利用

Posted: 2015年3月10日(火) 16:24
by がーまる
できました、ありがとうございます!

メモリについてはまだまだ勉強が必要だと実感しました。