C言語で配列の中身を数えるプログラムの処理が正常に行われない問題

このQ&Aのポイント
  • C言語で作成したプログラムで配列の中身を数える処理が正常に行われない問題が発生しています。
  • 特に、al[][7]の値が正しく計算されず、処理後に値が変わってしまいます。
  • 具体的な原因や修正方法についてのアドバイスをいただけると助かります。
回答を見る
  • ベストアンサー

配列の中身が変わってしまいます

英単語中のアルファベットの数を数えるプログラムをCで作っています。 ------------------------------------------------ #include<stdio.h> #include<stdlib.h> #include<string.h> #define NS 69964 /* 入力の最大数 */ #define WC 64 /* 入力の1行の文字数 */ int main(int argc, char *argv[]) { FILE *fp; char str[NS][WC]; int al[NS][26] = {0}; int i, j, k; //コマンドライン引数でテキストファイルを読み込む fp = fopen(argv[1],"r"); //テキストファイル内の単語を1行ごとにstr[]に格納 //改行文字は除き,大文字は小文字にする for(i = 0; i < NS; i++){ fgets(str[i], WC, fp); strtok(str[i], "\n"); for(j = 0; j < WC; j++) str[i][j] = tolower(str[i][j]); } printf("File reading completed.\n"); //カウントする //'a'~'z'の数がal[][0]~al[][25]の値に対応 //例えば'a'が2個,'z'が3個ならal[][0]=2,al[][25]=3 for(i = 0; i < 5; i++) for(j = 0; j < 20; j++) al[i][str[i][j] - 'a']++; //al[][]の中身を表示 for(i = 0; i < 5; i++) for(j = 0; j < 26; j++) printf("al[%d][%d]:%d\n", i, j, al[i][j]); fclose(fp); return 0; } -------------------------------------------------- for(i = 0; i < 5; i++) for(j = 0; j < 20; j++) al[i][str[i][j] - 'a']++; の処理を終えた後、al[][]の中身を見てると al[][7]の値だけがおかしいのです。'h'の数を数えているはずなのですが・・・ for文中でprintfしてみたのですがこのときは正しく数えられています。 ですがfor文を抜けた途端al[][7]の値が変わってしまいます。 これはどういうことなのでしょうか? 修正もしくは別の方法があれば教えてください。

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

  • ベストアンサー
  • chie65535
  • ベストアンサー率43% (8507/19342)
回答No.1

問題点 ・読み込んだ文字列が終端文字'\0'で終っているのを見ていない。 ↓ 「al[i][str[i][j] - 'a']++;」の時、str[i][j]が'\0'だった場合「al[i]['\0' - 'a']++;」が行われる。 'a'が97だと仮定とすると、al[i][-97]はal[i-1][-71]やal[i-2][-45]やal[i-3][-19]やal[i-4][7]と同じメモリ領域を指す。 この時、iが4であれば「al[i][str[i][j] - 'a']++;」は「al[0][7]++;」と等しい動作をする。 ↓ 結果、for文を抜けた途端、al[0][7]の値が1つ余計にインクリメントされる。 ・5行未満でEOFになった場合、EOFを見ていない。 ↓ str[]に何も読み込まないので、結果、終端文字'\0'で終っているのを見ていないのと同じような不正な動作をする。 ↓ 結果、for文を抜けた途端、al[0][7]の値が1つ余計にインクリメントされる。 従って「このプログラムは、質問者さんの期待通りの動作はしていないが、書いた通りの動作はしている」と言う事になる。 「質問者さんの期待通りの動作をさせたいなら、期待通りに動くように修正する必要がある」と言う事。 なお「al配列が配置されたメモリよりも前のメモリを、派手にぶち壊しまくっている」が、今回は、偶然にも「al配列の前に、str配列の末尾があった為、致命的な実行時例外が起きずに済んでいる」ようです。 メモリの配置がちょっとでも変われば「メモリを致命的に破壊し、実行時例外を起こし、プログラムがまったく動かない」と言う事が起きます。 「動いていたのは運が良かっただけ」ってのをお忘れ無く。

xwassyoix
質問者

お礼

>・読み込んだ文字列が終端文字'\0'で終っているのを見ていない。 >・5行未満でEOFになった場合、EOFを見ていない。 この2点を修正したら期待通りの動作をしました。 ありがとうございました。

その他の回答 (2)

  • chie65535
  • ベストアンサー率43% (8507/19342)
回答No.3

あと「strの中の文字がa~zじゃないのを見てない」ので、半角スペースとか混ざってたら、al[0][7]以外のデータも壊れるな。例えばal[0][13]とか。

  • chie65535
  • ベストアンサー率43% (8507/19342)
回答No.2

>結果、for文を抜けた途端、al[0][7]の値が1つ余計にインクリメントされる。 「1つ余計に」どころじゃねえwwwww strに読まれた文字列が5文字しか無かったら、6~20文字目の15回も余計にインクリメントされる。 つ~事で、 結果、for文を抜けた途端、al[0][7]の値がかなり余計にインクリメントされる。 が正しい。

関連するQ&A

  • ヒープソートがわかりません

    以下のようなヒープソートのプログラムを作ったのですが、どうしても動きません。 #include <stdio.h> #include <stdlib.h> #include <string.h> #include <time.h> #define NS 3000000 /* 入力の最大数 */ #define WC 64 /* 入力の1行の文字数 */ void down(int from, int to); void heapsort(int s[NS+1][WC], int n); void down(int from, int to){ int i, j; int val; int s[NS+1][WC]; i = from; val = s[from][WC]; while(i <= to/2){ j = i*2; if(j+1 <= to && s[j] > s[j+1]) j++; if(val <= s[j][WC]) break; s[i][WC] = s[j][WC]; i = j; } s[i][WC] = val; } void heapsort(int s[NS+1][WC], int n) { int i; int tmp; for(i = n/2; i >= 1; i--) down(i, n); for(i = n; i >=2; i--){ tmp = s[1][WC]; s[1][WC] = s[i][WC]; s[i][WC] = tmp; down(1, i-1); } } main(int argc, char *argv[]) { FILE *fp; char s[NS][WC], buff[WC]; int i=0,n=0; if ((argc==1) || (argc>=3)) { printf("Input filename\n"); exit(1); } fp=fopen(argv[1],"r"); while(fgets(buff, sizeof(buff),fp)!=NULL){ sscanf(buff,"%s",&s[i]); i=n; } printf("File reading completed.\n"); heapsort(s[NS+1][WC],n); for (i=0; i<n; i++) { if (strcmp(A1[i],A2[i])!=0) { printf("Results are incorrect!\n"); exit(1); } } printf("Results are correct.\n"); fclose(fp); exit(0); }

  • 漢字を配列に入れたいのですが

    漢字を配列に入れたいのですが、うまくいきません。 3列、60行のcsvファイルを読み込んで配列に入れようをしているのですが、1列目、2列目、3列目にある漢字をそれぞれ配列に入れようとしているのですが、出力するとうまくいかないんです。誰か教えてください。 #include <stdio.h> #include <stdlib.h> #include <string.h> #define MAXBUFFSIZE 256 #define MAXWORDS 15 int split(char* words[], int length, char* split_ch,char* str){ int i,j; for(i=0;i<length;i++){ if((words[i] = strtok(str,split_ch))==NULL)break; str=NULL; } return(i); } int main(int argc, char* argv[]){ if(argc !=2){ printf("入力エラー"); return(0); } FILE *fp; char *ll, *words[MAXWORDS], ch, buff[MAXBUFFSIZE]; int i,j; unsigned int data1[60], data2[60], data3[60]; if((fp =fopen(argv[1],"r"))==NULL){ printf("ファイルが開けません。\n"); } j=0; ll= fgets(buff,MAXBUFFSIZE,fp); while((ll= fgets(buff,MAXBUFFSIZE,fp)) != NULL){ split(words, MAXWORDS, ",",ll); data1[j] = words[0]; data2[j] = words[1]; data3[j] = words[2]; j++; } printf("%s\n%s\n%s\n", data1,data2,data3); }

  • C言語について教えてください

    ファイルの文を読み込み、I、Weなどの定めた単語の数を数えるプログラムを作りたいのですが、うまくいきません。 具体的な問題点は、単語の数を数える際、一致する単語があった場合、再び最初から文を見直すため、無限ループしてしまう。 We,WE、weなど大文字小文字の違いで単語が数えられないなどです。 #include <stdio.h> #include <stdlib.h> #include <string.h> int main( void ) { char filename[FILENAME_MAX]; int j=0; int k=0; int l=0; int m=0; int n=0; int o=0; int w; char str[50]; FILE *fp; gets(filename); fp = fopen(filename,"r"); { if(fp==NULL) { printf("ERROR"); return -1; } } fscanf(fp,"%50s",str); for(w=0;w<=j+k+l+m+n+o;w++) { if(strcmp("I",str)==0) { j++; } if(strcmp("We",str)==0) { k++; } if(strcmp("You",str)==0) { l++; } if(strcmp("He",str)==0) { m++; } if(strcmp("She",str)==0) { n++; } if(strcmp("They",str)==0) { o++; } } printf("I: %d\n",j); printf("We: %d\n",k); printf("You: %d\n",l); printf("He: %d\n",m); printf("She: %d\n",n); printf("They: %d",o); fclose(fp); return 0; }

  • cプログラミングについて

    以下はsample.txtというファイルを読み込み、辞書順に並べるプログラミングですが、どう正しく 直したらよいかわかりません。間違っている場所を指摘していただけたらと思います。 (間違えだらけで申し訳ありません) #include <stdio.h> #include <stdlib.h> #include <string.h> #define MAXLINE 500 void mysort(char *word[MAXLINE]) { int i,j; char *tmp; for(i=0;;i++){ for(j=i+1;; j++){ if(strcmp(word[j],word[i])==1){ tmp=word[i]; word[i]=word[j]; word[j]=tmp; } } } } int main(void) { int i; FILE *fp; char str[MAXLINE]; fp= fopen("sample.txt", "r"); if (fp == NULL) { printf("fopen error\n"); exit(1); } while(( fgets( str, MAXLINE, fp )) != NULL) mysort(str); for(i=0;; i++) printf("%s\n", str[i]); return 0; }

  • 2次元配列

    課題で、氏名をローマ字で入力し、2次元配列に格納するプログラムを作成するというのがでました。条件として、氏名の長さは10文字以下、最大件数は10件。1エントリ入力ごとに累計件数を表示し、10件目の入力が完了するか、氏名の一文字目に'0'が入力されたら入力を終了しデータを表示する。11文字以上入力されたら、先頭から10文字までを有効とし、11文字目以降を無視する。 改行のみの入力の場合、エラーメッセージを表示し、再入力させる。 初心者の私には、データの表示と、条件の処理の仕方がわかりません。 下記プログラムを上記の条件を満たすようにするには、どこを直したらよいか教えてください。 お願いします。 #include <stdio.h> #include <string.h> #define BUFFERSIZE 1024 main() { char str[10][BUFFERSIZE]; char c; int count = 0; int i; int j; int l[10]; /*氏名の入力*/ for (i = 0; i < 10; i++) { printf("氏名人力 : "); while ( (c = getchar()) != '\n' ) { if( count < BUFFERSIZE - 1 ){ str[i][count++] = c; } } str[i][count] = '\0'; printf("累計 : %d \n", i+1); } for (i = 0; i < 10; i++) { for (j = 0; j < 10; j++) { printf("%c",str[i][j]); } putchar('\n'); } return 0; }

  • どこがちがうのでしょうか?

    以下の二つのプログラムはユーザーが文字を入力し、80文字以下なら ピリオドを追加して表示するというものです。上はうまくいきますが、下はうまくいきません。なぜでしょうか? #include<string.h> #include<stdlib.h> int main() { char str[80]; int i; printf("文字列を入力してください。\n"); gets(str); if(strlen(str)<80) { for(i=strlen(str);i<79;i++) strcat(str,"."); } printf("%s",str); } #include<stdio.h> #include<string.h> #include<stdlib.h> int main() { char str[80]; int i; printf("文字列を入力してください。\n"); gets(str); if(strlen(str)<80) { for(i=strlen(str)+1;i<79;i++) str[i] = "."; } printf("%s",str); }

  • csvファイルを読み込んで二次元配列に格納したい

    200×250のある数字と文字の入力されたcsvファイル(またはtxtファイル)を読み込んで2次元配列に格納したいのです。 色々調べるとカンマの処理が必要ということがわかりましたが、どのようにソースを書けばよいかわかりません。使用言語はC言語です。 また、実際にcsvファイルを読み込むようにプログラムを書いてみましたが、すべて-858993460となって表示されます。 プログラミング初心者で勉強中なため困っています。 回答よろしくお願いします。 #include <stdio.h> #include <stdlib.h> #define row 200 #define column 250 int main() { int i, j; int data[row][column]; FILE *fp; fp = fopen("sample.csv", "r"); if (fp == NULL){ printf("ファイルがありません\n"); return 1; } for (i = 0; i < row; i++){ for (j = 0; j < column; j++){ fscanf(fp, "%lf", &data[i][j]); } } for (i = 0; i<row; i++){ for (j = 0; j < column; j++){ printf("%3d ", data[i][j]); } printf("\n"); } fclose(fp); return 0; }

  • 二次元配列による文字列の配列の受渡しについての質問です。

    二次元配列による文字列の配列の受渡しについての質問です。 #include <stdio.h> void print_pname(char str[][5], int n) { int i, j; for (i = 0; i < n; i++) { printf("str[%d] = \"", i); for (j = 0; str[i][j] != '\0'; j++) putchar(str[i][j]); printf("\"\n"); } } int main(void) { char ary[][5] = {"Lisp", "C", "Ada"}; print_pname(ary, sizeof(ary) / sizeof(ary[0])); return 0; } 上のプログラム中の関数print_pnameの引数char str[][5]についてですが char (*str)[5](配列のポインタ)と変更した場合にwarningが多数発生します。 これはどうしてでしょうか? また、上のプログラムを配列のポインタを使って変更することは可能でしょうか? 以上、よろしくお願いします。

  • C言語で、ファイルを読み込んで数字と名前に分けて配列に格納に関する質問

    C言語で、ファイルを読み込んで数字と名前に分けて配列に格納に関する質問です! ファイルを開いた後でエラーとなるのですが、何が足りないのでしょうか? ファイル内容 20 田中 10 鈴木 #include <stdio.h> #include <string.h> #include <stdlib.h> int main(int argc,char *argv[]) { FILE *fp; char str[256]; char *tp; int k,i=0; int num[10]; char na[10][20]; fp=fopen(argv[1],"r"); if(fp==NULL){ printf("ファイルを開けません\n"); return 1; }else{ printf("開けた\n"); } while(fgets(str,sizeof str,fp)!=NULL){ tp=strtok(str," "); num[i]=atoi(tp); tp=strtok(NULL," "); strcpy(na[i],tp); i++; } printf("%d\n%s\n",num[0],na[0]); printf("%d\n%s\n",num[1],na[1]); fclose(fp); return 0; }

  • 【c++】2進数の表示が上手くいきません

    サブ関数を利用して、-15~15までの値を1刻みで10進数16進数8進数2進数を表示するプログラムを作っているのですが悩んでいます。 以下のようにやってるのですがデバッグが上手くいきません。 どこが悪いのでしょうか? #include<stdio.h> void sub(int a) { int i,wc; for(i=31;i>=0;i--) { wc=(a>>i)&0x01; if(i%4==3) printf(" "); printf("%1d",wc); } printf("\n"); return; } int main(void) { int wc,a,s,j,b; printf("10進数,16進数,8進数,2進数\n"); for(s=-15;s<=15;s++) { printf("%d %x %o \n",s,s,s); printf("%d\n",sub(a)); } return 0; } 初心者なのでありえない間違えがあるかと思います。 ご教授お願いしますm(_ _)m