質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.48%
for

for文は、様々なプログラミング言語で使われている制御構造です。for文に定義している条件から外れるまで、for文内の命令文を繰り返し実行します。

ループ

ループとは、プログラミングにおいて、条件に合致している間、複数回繰り返し実行される箇所や、その制御構造を指します

C++

C++はC言語をもとにしてつくられた最もよく使われるマルチパラダイムプログラミング言語の1つです。オブジェクト指向、ジェネリック、命令型など広く対応しており、多目的に使用されています。

Q&A

解決済

2回答

3964閲覧

C++でforループがうまくいきません

yasunori_bam

総合スコア7

for

for文は、様々なプログラミング言語で使われている制御構造です。for文に定義している条件から外れるまで、for文内の命令文を繰り返し実行します。

ループ

ループとは、プログラミングにおいて、条件に合致している間、複数回繰り返し実行される箇所や、その制御構造を指します

C++

C++はC言語をもとにしてつくられた最もよく使われるマルチパラダイムプログラミング言語の1つです。オブジェクト指向、ジェネリック、命令型など広く対応しており、多目的に使用されています。

0グッド

0クリップ

投稿2016/05/08 12:32

###前提・実現したいこと
C++で簡単なタイピングゲームを作っています。
ファイルからランダムな行を読み込んで、","で分割して分割した2番目の単語を入力させるものです。
###発生している問題・エラーメッセージ
ループ中、時々p_word_afterの指す値がNULLになります。この時、p_word_beforeの指す文字列は一つ前のループのときと同じになっています。
ちなみに、rand()%55の値が同じである場合はp_word_afterに値が入っているので、これは原因ではないと思います。これはどういった現象なんでしょうか。
###該当のソースコード

c++

1int key=0,count,score=0,mistake=0,ran=0,line,add,word_num; 2int level=0; 3char *advise; 4unsigned i; 5char line_text[256]; 6char word_before[50][128]; 7char word_after[50][128]; 8char *p_word_before; 9char *p_word_after; 10FILE *fp; 11fp=fopen(<<filename>>,"r"); 12for(count=50;count>0;count--){ 13 rewind(fp); 14 p_word_before=word_before[count]; 15 p_word_after=word_after[count]; 16 int len; 17 srand((unsigned)time(NULL)); 18 line=0; 19 for(line=rand()%55;line>0;line--){ 20 fgets(line_text,256,fp); 21 } 22 p_word_before=strtok(line_text,","); 23 p_word_after=strtok(NULL,","); 24 system("cls"); 25 add=0; 26 while(*(p_word_after+add)!='\n'){ 27 add++; 28 } 29 *(p_word_after+add)='\0'; 30 printf("%s (%s) 残り%d単語\n",p_word_before,p_word_after,count); 31 i=0; 32 len=strlen(p_word_after); 33 while(i<len){ 34 if(_kbhit()){ 35 key=_getch(); 36 if(key==0||key==224)key=_getch(); 37 if(key==p_word_after[i]){ 38 printf("checked %c\n",key); 39 i++; 40 score++; 41 }else{ 42 mistake++; 43 } 44 fflush(stdin); 45 } 46 } 47}

###補足情報(言語/FW/ツール等のバージョンなど)
IDE : VisualStudio2012 for Desktop

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

HogeAnimalLover

2016/05/08 12:38

それはどこの行です発生していますか?strtokで発生しているならばこの仕様を調べれば良いと思います。(検索対象が見つからない場合にNULLが返る)
guest

回答2

0

ベストアンサー

こんにちは。

ざっと見る限り、yasunori_bamさんが記載されている現象が起こるソースのようには見えません。

rand()%55が0の時はfgets(line_text,256,fp);が実行されませんので、p_word_beforeに「前回」のfor(count)ループ時の値が入ることはありえますが、その時、p_word_afterにも前回と同じ値が入る筈なのでNULLにはならない筈です。

また、rand()%55が例えば5の時、p_word_afterに値が入ったり、NULLが入ったりするのですね?
それもちょっと考えにくいです。・・・①

ただ、もし、サブ・スレッドを使っていてサブ・スレッドでもstrtok()を使っていたら、何が起こるか分かりませんので、ご説明されたような事象が起きる可能性はあります。strtok()はスレッド安全ではありませんので。
しかし、ご提示された内容からサブ・スレッドを使っているとは思えません。

試しに、下記の直後で、line_text、p_word_before、p_word_afterを表示してみては如何でしょうか?
何か掴めそうな気がします。

p_word_before=strtok(line_text,",");

p_word_after=strtok(NULL,",");

ところで、①の現象はどのようにして確認されましたか? どうもその確認にミスがありそうな予感がします。
また、ファイルは55行以上存在し、その全ての行にはp_word_afterがNULLになることはない文字列が入っていることは確認できていますか? 例えば、p_word_afterがNULLにならない行を55行コピペしておけば簡単に条件を作れると思います。


因みに下記はミスですね?(特に影響はないと思いますが。)

p_word_before=word_before[count];

p_word_after=word_after[count];

countは50から始まりますが、 word_before[50][128], word_after[50][128]ですので、word_before[49][], word_after[49][]までしかありません。

投稿2016/05/08 13:45

Chironian

総合スコア23272

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

catsforepaw

2016/05/08 14:34

> その時、p_word_afterにも前回と同じ値が入る筈なのでNULLにはならない筈です。 strtok関数は渡された文字列バッファに書き込みを行います(区切り文字のところにnull終端を書き込む)。ですので、ファイルから読まなかった場合、バッファの内容は前回とは違うものになります。
Chironian

2016/05/08 14:40

あああ、そういえばそうでした。フォローありがとうございます。 ということは、rand()%55が0の時に限って発生しそうですね。 しかし、この質問を開いた時はまだcatsforepawさんの書き込みなかったのに... 書き込む前にチェックするようにしているのですが、時々忘れちゃいます。orz
yasunori_bam

2016/05/08 14:45

回答有り難うございます。①の現象はVisualStudioのデバッグモードで変数の値を確認しながら実行して結果を得ました。 ご提案いただいた通り、p_word_before,p_word_after,line_textを表示してみたところ、rand()%55のときline_textに前のループ(for(count+1))のときの分割後のline_textが入っていました。ほんとにありがとうごうざいました!
catsforepaw

2016/05/08 14:49

Chironian さん > 書き込む前にチェックするようにしているのですが、時々忘れちゃいます。 私もです……。
guest

0

ぱっと見て気になったことが一点。

for(count=50;count>0;count--){

この記述だと、ループの中ではcountが50から始まって最後に1になります。

p_word_before=word_before[count];
p_word_after=word_after[count];

それぞれの配列は[50]で確保されているため、添え字に指定可能な範囲は0~49です。ループ初回でcountに50が入っているので、配列の範囲を超えています。


しっかり見て気になった点を。

for(line=rand()%55;line>0;line--){
fgets(line_text,256,fp);
}

rand()%55は0になる可能性があります。その場合、条件判定でいきなりループ終了になるため、ループ内のfgetsが実行されません。
そうなると、line_textに正しい文字列が入らず、strtok関数が正しい結果を返さないのだと思います。

line=rand()%55 + 1とすれば良いと思います。


補足
strtok関数は、返すべき文字列がないとNULLを返します。前述のループでfgetsを実行しないまま抜けると、前回のバッファの内容が残っているわけですが、区切り文字が消されてしまっているので最初のstrtokで文字列全体を返してしまい、次のstrtokでは返すべき文字列がないという状況になったのだと思われます。

投稿2016/05/08 13:19

編集2016/05/08 14:43
catsforepaw

総合スコア5938

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問