C言語のバグが分かりません。どうすれば修正できるのか教えてください

このQ&Aのポイント
  • C言語の初心者が数字当てゲームを作っている最中にバグが発生しました。rand関数を使ってランダムな値を生成するプログラムを作成したのですが、一部の値が重複して表示されてしまいます。どうすればバグを修正できるのでしょうか?
  • プログラミング初心者がC言語の数字当てゲームを作っている最中にバグが発生しました。rand関数を利用してランダムな値を生成する部分で、一部の値が重複して表示されてしまいます。バグの原因と修正方法を教えてください。
  • C言語の初心者が数字当てゲームを作っていますが、rand関数を使用した部分でバグが発生しています。一部の値が重複して表示される現象が起きており、どのようにすれば修正できるのか教えていただきたいです。
回答を見る
  • ベストアンサー

バグが分かりません。

私はプログラミングの初心者で、C言語の勉強をしています。 まだ教科書などに書いてあるようなホントに簡単なプログラムしか書いた事が無いのですが、なんらかのゲームでも作ってC言語に慣れようと思い、数字当てゲームを作っている最中です。 そこで当てるべき数をtimeでsrandしたrand関数を使おうと考えており、まずはrand関数の振る舞いを見ようと簡単なプログラムを作ったのですがそこで自分には理解できない現象が起きてしまいました。 自分の作った簡単なプログラムのソースは #include <stdio.h> #include <stdlib.h> #include <time.h> #define MAX 4 #define NO_ERROR 0 int main(void) { int buf[MAX-1]; int i; srand((unsigned int)time(NULL)); /* 1:buf[0]~buf[3]にランダムな値を格納。そして毎回どんな値を格納したかを表示 */ for (i = 0; i < MAX; i++){ buf[i] = rand() % 9; printf("buf[%d] = %d\n", i, buf[i]); } /* 2:4つの数字を連続で出力して4桁の数字を作る */ for (i = 0; i < MAX; i++){ printf("%d",buf[i]); } printf("\n"); } と自分としては特に変哲のないように見えるのですが、実行結果が buf[0] = 2 buf[1] = 3 buf[2] = 1 buf[3] = 3 2313 buf[0] = 4 buf[1] = 8 buf[2] = 5 buf[1] = 8 buf[2] = 5 buf[0] = 4 buf[1] = 5 buf[2] = 6 buf[8] = 1556124 4563 buf[0] = 7 buf[1] = 5 buf[2] = 0 buf[7] = -1073747000 7503 buf[0] = 1 buf[1] = 6 buf[2] = 0 buf[8] = 1556124 1603 buf[0] = 0 buf[1] = 3 buf[2] = 4 buf[1] = 3 buf[2] = 8 buf[7] = -1073747000 0383 のように1の部分が余分に表示されてしまう場合があります。 4桁の数字は出来ているので、一応無視して進んでも良いのですがどうしてもしっくりこないのでこの場で質問させていただきました。 このバグの原因は何なのでしょうか?また、どのようにすればバグの修正が出来るのでしょうか? 長文失礼しました。 よろしくお願い致します。

質問者が選んだベストアンサー

  • ベストアンサー
  • kmee
  • ベストアンサー率55% (1857/3366)
回答No.1

MAX=4のとき > int buf[MAX-1]; で用意されるbuf[?]の?は、いくつからいくつでしょう? C言語の配列では、用意した範囲外の値を添字に使用しても、直接はエラーになりません。 例: buf[5] で宣言して、 buf[8] とする、等 しかし、その領域には別の変数が対応していたり、通常アクセスできない領域だったりすることもあり、結果として異常な状態になることがあります。 今回の場合、bufの宣言の直後に変数iの宣言があります。そのためbuf[X]が範囲を越えた場合、まず変数iが被害を受ける可能性があります。というか、実際に受けてます。

katu0dasi
質問者

お礼

>MAX=4のとき >> int buf[MAX-1]; >で用意されるbuf[?]の?は、いくつからいくつでしょう? あ、なるほど、そう言えばここでの宣言では配列の個数を宣言するんでしたね; 配列は[0]から始まるということを意識しすぎていました・・・ >今回の場合、bufの宣言の直後に変数iの宣言があります。そのためbuf[X]が範囲を越えた場合、まず変数iが被害を受ける可能性があります。というか、実際に受けてます。 こう言う時に宣言の順番等が関係してくるんですね。 分かりやすい解説ありがとうございます。

その他の回答 (2)

  • nora1962
  • ベストアンサー率60% (431/717)
回答No.3

「まだ教科書などに書いてあるようなホントに簡単なプログラムしか書いた事が無いのですが」 失礼な言い方になるかもしれませんが、配列の宣言と確保する領域のサイズを了解していないとすると、勉強の仕方か、参考にしている教科書の選択があまりよくない様な気がしてしまいます。 本気でC言語を身につけようとお考えでしたら、 ・教科書、参考書を吟味する。配列やポインタ、構造体、共用体についてもしっかり著述されているものを選ぶ。K&Rを買うなら、「アンサーブック」も購入するなどして、フォローできるようにしておく。 ・いきなりゲームプログラムに走るよりはアルゴリズムとデータ構造(探索、ソート、リスト(単方向・双方向)、トリー、平衡木)などをじっくりやったほうが後々為になると思います。その上で教科書のアルゴリズムの改善が出来ないかとかやってみたらいいのでは。 プログラムは作るのは簡単ですが、「正しく動いている」ことを挙証するのはかなり面倒です(ある入力データについては正しく動くが、特定の場合だけ誤った結果を返す、最悪クラッシュするなんてプログラム山ほどあります)。 小言のようですが、基本が大事です。少しずつでも積み重ねて立派なプログラマになってください。

katu0dasi
質問者

お礼

>失礼な言い方になるかもしれませんが、配列の宣言と確保する領域のサイズを了解していないとすると、勉強の仕方か、参考にしている教科書の選択があまりよくない様な気がしてしまいます。 自分は今大学生で、情報系の授業を受けており、配列や構造体、ポインタ等は習っていたのですが、春休みで完全にボケていたようです。 「配列の宣言では個数を宣言するけど、実際に作られるのは0~宣言した個数-1になる」という所なのに、「配列は[0]から作られる」ことを意識しすぎていました。 >いきなりゲームプログラムに走るよりはアルゴリズムとデータ構造(探索、ソート、リスト(単方向・双方向)、トリー、平衡木)などをじっくりやったほうが後々為になると思います。 学校でリストについては習いましたが、他に関してはまだ勉強してないですね・・・ ちなみにアルゴリズムやデータ構造について書いてあるお勧めの本等はあるでしょうか? 丁寧に教えていただき、ありがとうございます。

回答No.2

>一応無視して進んでも良いのですが 無視して進んでよいわけがありません。 プログラム完全に壊れちゃってます。 >int buf[MAX-1]; 4つのデータを格納するのに3つ分しかデータエリア定義してないからです。

katu0dasi
質問者

お礼

>無視して進んでよいわけがありません。 >プログラム完全に壊れちゃってます。 はい。未熟な発言失礼しました。 >int buf[MAX-1]; >4つのデータを格納するのに3つ分しかデータエリア定義してないからです。 あ、なるほど・・・ 配列は[0]から始まるということに囚われていて、宣言では個数を定義する事をスッカリ失念していました。 的確なご指摘ありがとうございます。

関連するQ&A

  • C言語のプログラムでおかしな動作をするのですが教えて頂けないでしょうか?

    VisualStudio2008使用しています。 問題は、サイコロを200回振ってその出た目の数の個数分*を表示するプログラムです。 サイコロの目はランダムで出しています。 次のプログラムは正常に動作するものです。 /* #include <stdio.h> #include<stdlib.h> #include<time.h> #define N 200 int DICE(int min,int max); int main() { int n,i,j; int y[7]={0}; srand((unsigned int)time(NULL)); for(i=0;i<N;i++){ n=DICE(1,6); y[n]++; } for(i=1;i<7;i++){ printf(" %2d: ",i); for(j=0;j<y[i];j++){ printf("*"); } printf("\n"); } return 0; } int DICE(int min,int max) { return min+(int)(rand()*(max-min+1.0)/(1.0+RAND_MAX)); } */ 次のプログラムが問題で、授業で先生が配列にはstaticをおまじないとしてつけないと暴走すると言われたので、つけて見ると明らかに間違ってると思われるプログラムで動作するのですが原因を教えて頂けないでしょうか? 以下問題のプログラム! 配列の前にstaticをつけたら、添え字をいくつにしても正常に動作します。普通は添え自分しか領域って確保されないですよね??? /* #include <stdio.h> #include<stdlib.h> #include<time.h> #define N 200 int DICE(int min,int max); int main() { int n,i,j; //以下が問題の配列宣言 static int y[2]={0}; srand((unsigned int)time(NULL)); for(i=0;i<N;i++){ n=DICE(1,6); y[n]++; } for(i=1;i<7;i++){ printf(" %2d: ",i); for(j=0;j<y[i];j++){ printf("*"); } printf("\n"); } return 0; } int DICE(int min,int max) { return min+(int)(rand()*(max-min+1.0)/(1.0+RAND_MAX)); } 質問の意味が正確に伝わらなかった場合は補足しますので、ご回答よろしくお願いします。

  • 精度を上げたいのですが…

    #include <stdio.h> #include <time.h> #include <stdlib.h> #define MAX 1000 main(void) { int i; float x1, x2, en, sum=0.0, s; srand( (unsigned)time( NULL ) ); for(i=0;i<MAX;i++) { x1=((float)rand()/(float)RAND_MAX); x2=((float)rand()/(float)RAND_MAX); if(en=(x1-0.5)*(x1-0.5)+(x2-0.5)*(x2-0.5)<=(0.5)*(0.5)) { sum++; } } s=sum/MAX; printf("円の面積:%15.6e\n",s); } この方法で円の面積を求めたんですが、もう少し精度を上げたいと思います。ただそのプログラムをどうやって書けばいいのかさえわからずとまどっています。円全体でなくその一部を考え、またその部分を簡単に面積が求められるようにわけるプログラムを組みたいのですがどのようにすればいいのか教えてください。

  • この円を分割して。。

    #include <stdio.h> #include <time.h> #include <stdlib.h> #define MAX 10000 void main(void) { int i; float x1, x2, en, sum=0.0, s; srand( (unsigned)time( NULL ) ); for(i=0;i<MAX;i++) { x1=((float)rand()/(float)RAND_MAX); x2=((float)rand()/(float)RAND_MAX); if(en=(x1-0.5)*(x1-0.5)+(x2-0.5)*(x2-0.5)<=(0.5)*(0.5)) { sum++; } } s=sum/MAX; printf("円の面積:%15.6e\n",s); } 円の面積を求める方法です。がもう少し精度をあげる工夫をしようと思うのですが、円を4分割した第一象限の部分に乱数をとばしその面積を求め、4倍することで求めたいのですがどうプログラムを変えたらいいか教えてください。

  • 乱数について

    プログラミングの授業で、各種ソートのプログラムを勉強しました。 srand (99);でランダムに数字を作っていると教わったのですが、この中の数字もランダムにしたい場合、どのようなプログラムに変えればいいのでしょうか? また、括弧内の数字で、どのようにランダムに数字をはき出しているのか知りたいです。 ~time.c~ #include <stdio.h> #include <stdlib.h> #include <time.h> #define MAX 250000 void insert_sort(int x[], int n); main() { int i , x[MAX] , n ; time_t start , end ; //列配列の選択 srand (99); for (i = 0 ; i<MAX ; i++) x[i] =rand() % MAX; n = MAX; start =clock(); //測定対象プログラム insert_sort (x ,n); end = clock(); printf("sort\n"); for(i =0 ; i < n ; i++ ) if ( i == i/100*100) printf("%d\n" , x[i]); printf ("%f sec\n" , (double)(end-start) /CLOCKS_PER_SEC); return 0; } ~insert.c~ void insert_sort(int x[], int n) { int i, j, tmp; for(i=1;i<n;i++){ for(j=i;j>0;j--){ if(x[j]<x[j-1]){ tmp = x[j]; x[j]=x[j-1]; x[j-1]=tmp; } else{ break; } } } }

  • c言語について

    #include <stdio.h> #include <stdlib.h> #include <time.h> #define D_ABC 50 int main( void ) { int abc[ D_ABC ]; int goukei; int homi; int p; int k; p = 0; printf( "指定する数字を入力して下さい。\n" ); do{ scanf( "%d", &abc[p++] ); } while( abc[p - 1] > 0 ); --p; srand( time(NULL)); do{ for( k = 0; k < 100; k++ ) { int o = rand() % p; int t = rand() % p; int buf; buf = abc[o]; abc[o] = abc[t]; abc[t] = buf; } goukei = 0; homi = 0; for( k = 0; k < 5; k++ ) { goukei += abc[k]; if( ( abc[k] % 2 ) == 0 ) ++homi; } } while( goukei < 50 || goukei > 110 || homi < 2 || homi > 3 ); for( k = 0; k < 5; k++ ) printf( "%d\t", abc[k] ); printf( "\n" ); return 0; } このソースプログラムはコンパイルできますが、複数パターンを出力させるにはどのように すればいいのかまだ勉強不足で少し困っているんですが教えてもらえないでしょうか。

  • 毎回違う乱数を生成するにはどうしたらいいでしょうか

    C言語の初心者です。よろしくお願いいたします! 乱数に関する質問:毎回違う乱数を生成するにはどうすればいいでしょうか。 学校の講義の中に  >>time() は1970 年1 月1 日0 時0 分(標準時)からの経過秒数を返 すため,1 秒以内に何度も実行すると,同じ数字で乱数を初期化す ることになり,結果も同じになってしまう. という記述がありますが、時間を置いてから、実行しても同じ結果となりました。 その一 #include <stdio.h> #include <stdlib.h> #include <math.h> #include <time.h> int main(void) { double x,y; int i,index=0; double a,n; printf("How many trials?..."); scanf("%lf",&n); for (i=0;i<n;i++) { x=rand()/(RAND_MAX+1.0); y=rand()/(RAND_MAX+1.0); if((x*x+y*y)<1) index++;} a=4*index/n; printf("Result is %.2f(%.2f)",a,sin(-a)); return 0; } その二 #include <stdio.h> #include <stdlib.h> #include <time.h> int main(void) { int a; srand((unsigned)time(NULL)); a=(int)(rand()/(RAND_MAX+1.0)*10); printf("%d\n",a); return 0; } お忙しい中、教えていただけたらうれしいです。

  • srand(time(NULL))の使い方

    C言語の勉強中で「ランダムな整数値を作成し、その数値が偶数か奇数かを判定するプログラムをif~else 構文を使って作成」したいのですが下記のプログラムでやってみたのですがまだまだ知識不足で完成しません。知識をお借りできればと思っていますのでどなたか教えてください。 #include<stdio.h> #include<stdlib.h> #include<time.h> int main(void) { { int = i; i = rand(); srand((unsigned)time(NULL)); printf("%d\n",rand()); } if(i % 2 == 0) { printf("偶数である:\n",i); } else { printf("奇数である:\n",i); } return 0; }

  • C言語でこのプログラムを完成させるには

    C言語でこのプログラムを完成させるには C言語初心者です。 1~6の乱数を100回発生させて、それぞれの出現回数をカウントし、ヒストグラムとして表示するプログラムを作成したいのですが上手くいきません。 #include <stdio.h> #include <time.h> #include <stdlib.h> int rnd(int m, int n) { return (int)(n-m+1)*(rand()/(RAND_MAX+0.1))+m; } int main(void) { int i, j, r; int hist[7]; for (i=1; i<7; i++) hist[i]=0; srand((unsigned)time(NULL)); for (i=0; i<100; i++) { r=6; while (6-- > 0) putchar('*'); putchar('\n'); } for (i=1; i<7; i++) { printf("%2d:", i); for (j=0; j<hist[i]; j++) printf("*"); printf("\n"); } return 0; } 何がいけないのでしょうか? よろしくお願いします。

  • 無限

    今、ランダムな数を無限に出すプログラムを作っています。一応 #include <stdio.h> #include <time.h> #include <stdlib.h> main () { int word,i; srand (time(NULL)); word=rand(); printf("number!!\n"); for (i=0;i<3;i--) { printf("%d\n",word); } return 0; } というかんじで作ったのですが、コンパイルして実行すると、同じ数しか出てきません。どうすればいいか教えてください。

  • バグが直りません

    山手線すごろくのプログラムをC言語で作ろうと思い、以下のように作りました。 #include<stdio.h> #include<stdlib.h> #include<time.h> #include<conio.h> char st[][80]={"東京", "神田", "秋葉原", "御徒町", "上野", "鶯谷", "日暮里", "西日暮里", "田端", "駒込", "巣鴨", "大塚", "池袋", "目白", "高田馬場", "新大久保", "新宿", "代々木", "原宿", "渋谷", "恵比寿", "目黒", "五反田", "大崎", "品川", "田町", "浜松町", "新橋", "有楽町", "東京"}; int now=0; int dicenum=1; int pt=30; int stlength=30; int dice(void){ int dc; srand(time(NULL)); dc=rand()%6+1; return dc; } int event(void){ int eve; srand(time(NULL)); eve=rand()%9+1; return eve; } void step(void){ now++; } int turn(void){ int i, d, ev; for(i=1;i<=dicenum;i++){ d+=dice(); } printf("ダイス目は%dです。\n", d); for(i=1;i<=d;i++){ now++; printf("%s\n", st[now]); if(now==stlength){ return 1; } } ev=event(); switch(ev){ case 1: printf("プラス10pt\n"); pt+=10; break; case 2: printf("プラス20pt\n"); pt+=20; break; case 3: printf("プラス50pt\n"); pt+=50; break; case 4: printf("マイナス10pt\n"); pt-=10; break; case 5: printf("マイナス20pt\n"); pt-=20; break; case 6: printf("マイナス50pt\n"); pt-=50; break; case 7: printf("急行\n"); dicenum=2; break; case 8: printf("特急\n"); dicenum=3; break; case 9: printf("新幹線\n"); dicenum=4; break; } if(pt<=0){ return 2; }else{ return 0; } } int main(){ srand(time(NULL)); int status=0; char a; printf("鉄道すごろく(JR山手線)\n"); printf("エンターキーでスタート\n"); scanf("%c", &a); while(1){ while(getch()!='\r'){ ; } if(status==0){ printf("現在地:%s\n%dポイント\n", st[now], pt); status=turn(); }else if(status==1){ printf("ゴール!おめでとう!\n"); break; }else if(status==2){ printf("残念!ポイントがなくなりました。ゲームオーバー。\n"); break; } } return 0; } コンパイルは通ったのですが実行すると"ダイス目は1873695976です。"などとダイス目がものすごく大きな値になってしまいます。原因が全く分からないのでどなたかよろしくお願いします。

専門家に質問してみよう