線形リストの先頭移動

フォーラム(掲示板)ルール
フォーラム(掲示板)ルールはこちら  ※コードを貼り付ける場合は [code][/code] で囲って下さい。詳しくはこちら
アバター
amehirune
記事: 181
登録日時: 5年前
住所: どっか
連絡を取る:

線形リストの先頭移動

#1

投稿記事 by amehirune » 3年前

こんにちは、アメヒルネというものです。
本日は、線形リスト構造に関する質問をさせていただきます。

C言語を用いて、リストを用いた英単語帳を作ろうと思い立ち、
実際に作ったモノがこちらです。

コード:

struct word_t{
	char ques[64];
	char ans[64];
	struct word_t *next;
}*head;

// 領域確保
struct word_t *talloc(){
	return ( struct word_t * )malloc( sizeof( struct word_t ) );
}

// 領域開放
void word_free( struct word_t *p ){
	
	struct word_t *q;
	if( p != NULL ){
		q = p->next;
		free( p );
		word_free( q );
	}

}

int main(int argv,char *argc[]){

	int num, right, wrong;
	char filename[256], answer[64];
	struct word_t *p, *old, *pd, *oldd;
	FILE *fp;

	if( argv<2 ){
		printf( "実行ファイル名を入力してください ≫" );
		scanf( "%s", filename );
	}
	else{
		strcpy( filename, argc[1] );
	}

	if( ( fp = fopen( filename, "r" ) ) == NULL ){
		printf( "実行ファイルが見つかりませんでした\n" );
		printf( "ファイルの確認をしてください\n" );
		exit( EXIT_FAILURE );
	}

	head = ( struct word_t * )realloc( head, sizeof( struct word_t ) );
	fscanf( fp, "%s %s", head->ques, head->ans );
	head->next = NULL;

	num = 1;
	old = head;
	while( p = talloc(), fscanf( fp, "%s %s", p->ques, p->ans ) != EOF ){
		old->next = p;
		p->next = NULL;
		old = p;
		num++;
	}
	fclose( fp );

	p = old = head;
	right = wrong = 0;
	for(int i=0;p!=NULL;i++){
		system( "cls" );
		printf( "意味:%s\n" ,p->ques );
		printf( "解答:" );		
		scanf( "%s", answer );		
		getchar();

		system( "cls" );
		printf( "意味:%s\n", p->ques  );
		printf( "解答:%s\n", answer   );
		printf( "正解:%s  ", p->ans );
		if( strcmp( p->ans, answer )==0 ){
			printf( "○\n" );
			right++;
			old = p;
			p = p->next;
		}
		else{
			printf( "×\n" );
			wrong++;
			if( p!=head ){
				oldd = old;
				pd = p;
				//old = p;
				p = p->next;
				oldd->next = pd->next;
				pd->next = head;
				head = pd;
			}
			else{
				old = p;
				p = p->next;
			}
		}
		printf( "\t\t\t正解数 %d/総問題数 %d\n", right, i+1 );
		getchar();
	}

	if( wrong==0 ){
		printf( "パーフェクトです!\n" );
		printf( "この調子で頑張ってください!\n" );
	}

	while( wrong>0 ){

		system( "cls" );
		printf( "間違えた部分の復習プログラムを開始します\n" );		getchar();

		p = head;
		num = wrong;
		right = wrong = 0;
		for(int i=0;i<num;i++){
			system( "cls" );
			printf( "意味:%s\n" ,p->ques );
			printf( "解答:" );		
			scanf( "%s", answer );		
			getchar();

			system( "cls" );
			printf( "意味:%s\n", p->ques  );
			printf( "解答:%s\n", answer   );
			printf( "正解:%s  ", p->ans );
			if( strcmp( p->ans, answer )==0 ){
				printf( "○\n" );
				right++;
				old = p;
				p = p->next;
			}
			else{
				printf( "×\n" );
				wrong++;
				if( p!=head ){
					oldd = old;
					pd = p;
					//old = p;
					p = p->next;
					oldd->next = pd->next;
					pd->next = head;
					head = pd;
				}
				else{
					old = p;
					p = p->next;
				}
			}
			printf( "\t\t\t正解数 %d/総問題数 %d\n", right, i+1 );
			getchar();
		}
	}

	fp = fopen( filename, "w" );
	p = head;
	while( p!=NULL ){
		fprintf( fp, "%s %s\n", p->ques, p->ans );
		p = p->next;
	}
	fclose( fp );

	word_free( head );

}
※includeは必要な分だけ済ませてあると思ってください

私はこの英単語帳に、

①A面に記載されている内容が表示される
②B面に記載されていると考えられる単語を入力する
③正誤判定(正解ならば⑤は飛ばす)
  ④カーソルを次に回す
  ⑤間違えた単語(1つ前のカード)を先頭へ移動する(再編成?)
⑥上記内容を、カードがなくなるまで繰り返す
⑦間違えた単語のみで①~⑥を行う
⑧間違えた単語がなくなるまで⑦を行う

という動作を期待して作りました。
しかし、動作⑦において、3問以上不正解だった場合、
再テスト3枚目のカードがNULLとなるバグが発見されました。
恐らく再編成する過程でどこかミスを犯しているのだろうとは思いますが、
私にはそれ以上のことが分かりません。

しかも、もっと厄介なことに、3問以上間違えたというバグの起こる状況であるにも拘らず、
期待通りに動作する場合もあるのです。
この、状況は全く同じなのに「起こったり起こらなかったりする」バグというのがどうにも不安で、質問させていただきました。
どうかよろしくお願いします。
開発環境はVC++2008EEです。
► スポイラーを表示
ほら、来いよ!! 誤字や矛盾を指摘したい奴から、前に出てこいよぉおおおおおおおッ!!!
※都合により、不定期でしか現れません。即返などはできませんのでご了承ください※

アバター
usao
記事: 1538
登録日時: 6年前

Re: 線形リストの先頭移動

#2

投稿記事 by usao » 3年前

> head = ( struct word_t * )realloc( head, sizeof( struct word_t ) );

realloc()に,いきなり値不定なポインタ渡してるのは危険なのではないでしょうか.

(realloc()に,malloc()等で割り当てた領域を指さない(NULLでない)ポインタを渡すのはまずいのでは)

たいちう
記事: 418
登録日時: 8年前

Re: 線形リストの先頭移動

#3

投稿記事 by たいちう » 3年前

> この、状況は全く同じなのに「起こったり起こらなかったりする」
> バグというのがどうにも不安で、質問させていただきました。
> どうかよろしくお願いします。

単なる再現性の低いバグでしょう。
なぜデバッグしないのですか?

私ならばリストの内容を表示する関数を作り、
並び替えの前後に表示してみます。
またデータの数も5件程度でデバッグします。

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

Re: 線形リストの先頭移動

#4

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

usao さんが書きました:> head = ( struct word_t * )realloc( head, sizeof( struct word_t ) );

realloc()に,いきなり値不定なポインタ渡してるのは危険なのではないでしょうか.

(realloc()に,malloc()等で割り当てた領域を指さない(NULLでない)ポインタを渡すのはまずいのでは)
確かに不定の値を持ったポインタを渡すのはまずい(未定義動作)ですが、
headは明示的に初期化されていないグローバル変数なのでNULLに初期化され、reallocの第一引数にNULLを渡すとmallocと等価になるので、mallocに統一されていないのは気持ち悪いですが安全です。

気持ち悪いついでに、
amehirune さんが書きました:※includeは必要な分だけ済ませてあると思ってください
どうしてこんな気持ち悪い仮定を要求するのですか?
どうして素直にincludeを書かないのですか?
デバッグにおいて思い込みは厳禁です。
複雑な問題?マシンの性能を上げてOpenMPで殴ればいい!(死亡フラグ)

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

Re: 線形リストの先頭移動

#5

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

amehirune さんが書きました:しかも、もっと厄介なことに、3問以上間違えたというバグの起こる状況であるにも拘らず、
期待通りに動作する場合もあるのです。
この、状況は全く同じなのに「起こったり起こらなかったりする」バグというのがどうにも不安で、質問させていただきました。
本当に状況は全く同じなのですか?
同じ(例えば)3問不正解でも、連続かそうでないか、最初からか2問目以降からか、などの違いがある、ということも無いですか?
複雑な問題?マシンの性能を上げてOpenMPで殴ればいい!(死亡フラグ)

アバター
usao
記事: 1538
登録日時: 6年前

Re: 線形リストの先頭移動

#6

投稿記事 by usao » 3年前

>headは明示的に初期化されていないグローバル変数なのでNULLに初期化され

フォローありがとうございます.
二度と馬鹿なことを書かないように以後徹底的に気を付けようと思います.

アバター
amehirune
記事: 181
登録日時: 5年前
住所: どっか
連絡を取る:

Re: 線形リストの先頭移動

#7

投稿記事 by amehirune » 2年前

皆様、ご返信ありがとうございます。
usao さんが書きました:> head = ( struct word_t * )realloc( head, sizeof( struct word_t ) );

realloc()に,いきなり値不定なポインタ渡してるのは危険なのではないでしょうか.

(realloc()に,malloc()等で割り当てた領域を指さない(NULLでない)ポインタを渡すのはまずいのでは)
みけCATさんもおっしゃっておられましたが、構文的に問題はないが「気持ち悪い書き方」ということで修正させていただきます。
ご指摘ありがとうございます。
たいちう さんが書きました: 単なる再現性の低いバグでしょう。
なぜデバッグしないのですか?

私ならばリストの内容を表示する関数を作り、
並び替えの前後に表示してみます。
またデータの数も5件程度でデバッグします。
>なぜデバッグしないのか
私の方で、知識は乏しいなりにできるだけデバッグは行ったのですが、それでも原因が分からなかったので質問させていただきました。
これに関しては私の説明不足です。申し訳ありません。
>リスト表示
実装してみました。現在のところ、再現を取ろうにもなかなか取れていません。
何かわかりましたらすぐに投稿します。
みけCAT さんが書きました: 気持ち悪いついでに、
amehirune さんが書きました:※includeは必要な分だけ済ませてあると思ってください
どうしてこんな気持ち悪い仮定を要求するのですか?
どうして素直にincludeを書かないのですか?
デバッグにおいて思い込みは厳禁です。
ええと、実はこのプログラムでは自作ヘッダをincludeしておりまして、
そちらでstdio、stdlib、string、math、malloc、time、conio、windowsをincludeしているのです。
それを転写するのが面倒になり、省いてしまいました。申し訳ありません。
みけCAT さんが書きました:
amehirune さんが書きました:しかも、もっと厄介なことに、3問以上間違えたというバグの起こる状況であるにも拘らず、
期待通りに動作する場合もあるのです。
この、状況は全く同じなのに「起こったり起こらなかったりする」バグというのがどうにも不安で、質問させていただきました。
本当に状況は全く同じなのですか?
同じ(例えば)3問不正解でも、連続かそうでないか、最初からか2問目以降からか、などの違いがある、ということも無いですか?
なるほど、確かにそれに関しては全く調べていませんでした…
検証させていただきます。が、「あるパターン」が原因だとしたらどう対処すればいいのか、
というか、なぜそのようなバグが発生するのかもわかりません…
ほら、来いよ!! 誤字や矛盾を指摘したい奴から、前に出てこいよぉおおおおおおおッ!!!
※都合により、不定期でしか現れません。即返などはできませんのでご了承ください※

たいちう
記事: 418
登録日時: 8年前

Re: 線形リストの先頭移動

#8

投稿記事 by たいちう » 2年前

> >リスト表示
> 実装してみました。現在のところ、再現を取ろうにもなかなか取れていません。

リスト表示が正しく実装できている保証もありません。
行き詰ったら、もろもろ最新のコード一式をあげてみてはどうでしょうか。

最小のデータと最小の手順で、不具合が再現する方法を見つけるのが大事です。

アバター
amehirune
記事: 181
登録日時: 5年前
住所: どっか
連絡を取る:

Re: 線形リストの先頭移動

#9

投稿記事 by amehirune » 2年前

変身が遅れました、申し訳ありません。
ひとまず、リスト表示のコードも合わせて再掲します。

コード:

#define ONCE
#include "origin.h"//自作ヘッダなのであまり深く気にしないでください 前述した標準ライブラリがインクルードされています

struct word_t{
	char ques[64];
	char ans[64];
	struct word_t *next;
}*head;

// 領域確保
struct word_t *talloc(){
	return ( struct word_t * )malloc( sizeof( struct word_t ) );
}

// 領域開放
void word_free( struct word_t *p ){
	
	struct word_t *q;
	if( p != NULL ){
		q = p->next;
		free( p );
		word_free( q );
	}

}

void printlist(){

	struct word_t *check;

	check = head;
	while( check->next!=NULL ){
		printf("%s\n",check->ans);
		check = check->next;
	}

}

int main(int argv,char *argc[]){

	int num, right, wrong;
	char filename[256], answer[64];
	struct word_t *p, *old, *pd, *oldd;
	FILE *fp;

	if( argv<2 ){
		printf( "実行ファイル名を入力してください ≫" );
		scanf( "%s", filename );
	}
	else{
		strcpy( filename, argc[1] );
	}

	if( ( fp = fopen( filename, "r" ) ) == NULL ){
		printf( "実行ファイルが見つかりませんでした\n" );
		printf( "ファイルの確認をしてください\n" );
		exit( EXIT_FAILURE );
	}

	head = ( struct word_t * )malloc( sizeof( struct word_t ) );
	fscanf( fp, "%s %s", head->ques, head->ans );
	head->next = NULL;

	getchar();
	num = 1;
	old = head;
	while( p = talloc(), fscanf( fp, "%s %s", p->ques, p->ans ) != EOF ){
		old->next = p;
		p->next = NULL;
		old = p;
		num++;
		system("cls");
		printf("old    [%p]\n",old);
		printf("head   [%p]\n",head);
		printf("p      [%p]\n",p);
		printf("p->next[%p]\n",p->next);
		printf("num    [%d]\n",num);
		getchar();
	}
	fclose( fp );



	p = old = head;
	right = wrong = 0;
	for(int i=0;p!=NULL;i++){
		system( "cls" );
		printf( "意味:%s\n" ,p->ques );
		printf( "解答:" );		
		scanf( "%s", answer );		
		getchar();

		system( "cls" );
		printf( "意味:%s\n", p->ques  );
		printf( "解答:%s\n", answer   );
		printf( "正解:%s  ", p->ans );
		if( strcmp( p->ans, answer )==0 ){
			printf( "○\n" );
			right++;
			old = p;
			p = p->next;
		}
		else{
			printf( "×\n" );
			wrong++;
			if( p!=head ){
				oldd = old;
				pd = p;
				//old = p;
				p = p->next;
				oldd->next = pd->next;
				pd->next = head;
				head = pd;
			}
			else{
				old = p;
				p = p->next;
			}
		}
		printf( "\t\t\t正解数 %d/総問題数 %d\n", right, i+1 );
		printlist();
		getchar();
	}

	if( wrong==0 ){
		printf( "パーフェクトです!\n" );
		printf( "この調子で頑張ってください!\n" );
	}

	while( wrong>0 ){

		system( "cls" );
		printf( "間違えた部分の復習プログラムを開始します\n" );		getchar();

		p = head;
		num = wrong;
		right = wrong = 0;
		for(int i=0;i<num;i++){
			system( "cls" );
			printf( "意味:%s\n" ,p->ques );
			printf( "解答:" );		
			scanf( "%s", answer );		
			getchar();

			system( "cls" );
			printf( "意味:%s\n", p->ques  );
			printf( "解答:%s\n", answer   );
			printf( "正解:%s  ", p->ans );
			if( strcmp( p->ans, answer )==0 ){
				printf( "○\n" );
				right++;
				old = p;
				p = p->next;
			}
			else{
				printf( "×\n" );
				wrong++;
				if( p!=head ){
					oldd = old;
					pd = p;
					//old = p;
					p = p->next;
					oldd->next = pd->next;
					pd->next = head;
					head = pd;
				}
				else{
					old = p;
					p = p->next;
				}
			}
			printf( "\t\t\t正解数 %d/総問題数 %d\n", right, i+1 );
			getchar();
			printlist();
		}
	}

	fp = fopen( filename, "w" );
	p = head;
	while( p!=NULL ){
		fprintf( fp, "%s %s\n", p->ques, p->ans );
		p = p->next;
	}
	fclose( fp );

	word_free( head );

}
ほら、来いよ!! 誤字や矛盾を指摘したい奴から、前に出てこいよぉおおおおおおおッ!!!
※都合により、不定期でしか現れません。即返などはできませんのでご了承ください※

たいちう
記事: 418
登録日時: 8年前

Re: 線形リストの先頭移動

#10

投稿記事 by たいちう » 2年前

> ひとまず、リスト表示のコードも合わせて再掲します。

リスト表示、間違ってますよね。
最後の要素まで確認してますか?


> 前述した標準ライブラリがインクルードされています

だから、この部分も疑っているわけです。
多分大丈夫なのでしょうけど、「多分」を取り除きたいのです。

アバター
amehirune
記事: 181
登録日時: 5年前
住所: どっか
連絡を取る:

Re: 線形リストの先頭移動

#11

投稿記事 by amehirune » 2年前

>表示できていない
そうでした…
check!=NULLにすべきでしたね。

>多分
詳細部分を掲載します。

コード:

#define _CRT_SECURE_NO_WARNINGS

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<time.h>
#include<math.h>
#include<malloc.h>
#include<conio.h>
#include<windows.h>

#define DrawLines() printf("-------------------------------------------------------------------------------\n")

#define PRINT_COLOR_RED    12
#define PRINT_COLOR_BLUE    9
#define PRINT_COLOR_YELLOW 14
#define PRINT_COLOR_GREEN  10
#define PRINT_COLOR_WHITE  15
#define PRINT_COLOR_BLACK   0
#define PRINT_COLOR_PURPLE 13

#ifdef ONCE
// 必要事項が出て来たらいろいろと書き込む
#endif
ほら、来いよ!! 誤字や矛盾を指摘したい奴から、前に出てこいよぉおおおおおおおッ!!!
※都合により、不定期でしか現れません。即返などはできませんのでご了承ください※

アバター
amehirune
記事: 181
登録日時: 5年前
住所: どっか
連絡を取る:

Re: 線形リストの先頭移動

#12

投稿記事 by amehirune » 2年前

他にすべき問題があり、現状この問題には手が出せない状況なので、
ひとまず保留ということにさせていただきます。
ほら、来いよ!! 誤字や矛盾を指摘したい奴から、前に出てこいよぉおおおおおおおッ!!!
※都合により、不定期でしか現れません。即返などはできませんのでご了承ください※

閉鎖

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