- ベストアンサー
ファイルを読み込むプログラム
氏名、英語、数学が保存されているファイル(記入されている人数は不明とし100人まで読み込めるとする)を関数内で読み込んで個人の点数の平均値と、皆の数学の平均値、英語の平均値を関数内で定義して出力するプログラムを作りましたが、コンパイルはできても実行するとエラーが出ます。どこがおかしいか分かる人教えてください。 #include <stdio.h> #include<stdlib.h> #include<string.h> #define number 100 int n=0; //構造体を宣言する struct student {char Name[6]; double Math; double Eng; }; struct student *sset_student(char *buffer) { struct student *pss[number]; int ret; ret=sscanf(&buffer[0],"%s %lf %lf",&pss[0]->Name,&pss[0]->Math,&pss[0]->Eng); if(ret!=3) { puts("代入された入力項目の個数が3でありません"); return NULL; } return pss[0]; } struct student *fset_all_student(void) { int i; struct student *pss[number]; FILE *fpin; char buffer[20]; fpin=fopen("input.txt","r"); if(fpin==NULL) { fprintf(stderr, "入力するファイルが開きません"); return NULL; } while(fgets(&buffer[0],sizeof(buffer),fpin) !=NULL ) { pss[i]=sset_student(&buffer[0]); i++; } n=i; fclose(fpin); return pss[0]; } void get_student_average(struct student *pss) { double heikin; heikin=(pss->Math+pss->Eng)/2.0; printf("%sの平均点は%dです\n",pss->Name,heikin); } void get_average(struct student *pss) { int i; double msum=0; double esum=0; for(i=0;i<3;i++) { msum+=pss[i].Math; esum+=pss[i].Eng; } printf("数学の平均点は%dです\n",msum/n); printf("英語の平均点は%dです\n",esum/n); } int main(void) { int i; struct student *a[number]; for(i=0;i<n;i++) { a[i]=fset_all_student(); } for(i=0;i<n;i++) get_student_average(a[i]); get_average(a); return 0; }
- みんなの回答 (9)
- 専門家の回答
質問者が選んだベストアンサー
構造体配列と、構造体へのポインタの配列を混同しているようですね。 struct student *pss[number]; struct student *a[number]; は、ポインタの配列なので、実体がありません。 ここに、名前、英語の点数、数学の点数を代入するだけで、 メモリー(この場合はスタック)が破壊されてしまいます。 それで、強制終了しちゃうんですね。 グローバル変数で、 struct student StudentTable[number]; として、定義し、ここに、読みこんだ名前,英語の点数、数学 の点数を代入していけばOKになるんじゃないかな・・ #include <stdio.h> #include <string.h> #define number 100 struct student { char name[80]; float Math; float Eng; float MEAve; }; struct student StudentTable[number]; char readbuf[256]; /* 文字列(text)からカンマで区切られた文字列を取り出す */ char *ExtractToken(char *cp, char *text) { while(*text !='\0') { if( *text == ',' ) { text++; break; } *cp++ = *text++; } *cp = "\0"; return( text ); } /* 指定されたカンマで区切られた文字列から、 生徒の名前、英語の点数、数学の点数をセットする */ void SetResult(char *readstr, struct student *stp) { char buf[128], *cp; cp = readstr; /*名前の抽出*/ cp = ExtractToken( buf, cp ); strcpy( stp->name, buf ); /* 英語の点数の抽出 */ cp = ExtractToken( buf, cp ); stp->Eng = atof(buf); /* 数学の点数の抽出 */ cp = ExtractToken( buf, cp ); stp->Math = atof(buf); } /* ファイルから生徒の名前、点数を読み出して、 テーブル(最大100名)を作成する 関数の戻り値:読み込んだ生徒数 */ int SetAllResults(char *filename) { FILE *fp; int i; struct student *stp; char *rstr; fp = fopen(filename, "rt"); if( fp == NULL ) { fprintf(stderr,"ファイルがオープンできません\n"); return(0); } stp = StudentTable; /* stp=&StudentTable[0];と同じ*/ for(i=0; i<number i++) { rstr = fgets(readbuf,sizeof(readbuf),fp); if( rstr == NULL ) break; SetResult(readbuf, stp); stp++; } fclose(fp); /* 読み込んだ人数 */ return( i ); } /* 個人の平均点 */ float GetStudentAve( struct student *stp ) { stp->MEAve = (stp->Math + stp->Eng) / 2.0; return( stp->MEAve ); } /* 全体の数学の平均 */ float GetMathAve(int Count) { struct student *stp; int i; float = sum; stp = StudentTabe; sum = 0.0; for(i=0; i<Count; i++) { sum += stp->Math; stp++; } return( sum / (float)Count ); } /* 英語の平均 */ float GetMathAve(int Count) { /* 省略 */ } int main() { int n, i; struct student *stp; float ave; n = SetAllResults("input.txt"); if( n == 0 ) { return(1); } stp = StudentTable; /* stp=&StudentTable[0];と同じ*/ for(i=0; i<n; i++) { ave = GetStudentAve( stp ); printf("%sさんの平均点は、%4.1fです\n", ave); } printf("\n"); /*1行空ける*/ printf("数学の平均は、%4.1fです\n", GetMathAve(n)); printf("英語の平均は、%4.1fです\n", GetEngAve(n)); }
その他の回答 (8)
- tig33
- ベストアンサー率50% (6/12)
>ほんと回答ありがとうございます!! >float GetMathAve(int Count) >のstp = StudentTabe;が未定義のシンボル >char *ExtractToken(char *cp, char *text) > *cp = "\0";の移植性のないポインタ変換 >とエラーがでてしまいます… >この場合すればいいですか? 未定義のシンボル→ stp = StudentTable; *cp = "\0"; → *cp = '\0'; と修正してください。
補足
なおりました。 回答ありがとうございます!!!
- tig33
- ベストアンサー率50% (6/12)
GetMathAve関数の中で定義している float = sum は、間違いです。 float sum; が正解です。 失礼しました。 他にもケアレスミスがあるかも。。。大目に見てやってくださいm(__)m
- yama5140
- ベストアンサー率54% (136/250)
☆設計上の根本的な誤り:人数分ファイルを開閉して、先頭レコード( yuki 65 70 )の情報のみを利用しようとしています。 ☆構造体を、struct student *a[number]; のように * 付きで宣言してますが・・。 ☆最初の関数で、struct student *pss[number]; としてるけど、[0] しか使っていないのだから・・。 ☆ n が、最初に使われるのは?。 ☆printf("%sの平均点は%dです\n",pss->Name,heikin); 【 %d 】? (他にも) ☆ &buffer[0] は単に、buffer でいいと思うけど。 ---------------------------------------- 暇な年寄りが、関数間で構造体をやりとりしない方法で、整形してみました。 こんなんもあるよ、程度にご覧下さい。 #include <stdio.h> #include <stdlib.h> #define NUMBER 100 typedef struct{ char cName[8]; // 8, 16, 32・・, 趣味の世界 float fMth; float fEng; }STUDENT; STUDENT sSeit[NUMBER]; // グローバル void Error( char *cMess ) { fprintf( stderr, cMess ); exit( 0 ); } int fset_all_student( void ) { int iNo = 0, iRet; FILE *fp; char cBuf[32]; // 32, 64, 128・・ fp = fopen( "input.txt", "r" ); if( NULL == fp ) Error( "input.txt をオープンできない\n" ); while( NULL != fgets( cBuf, 32, fp ) ){ iRet = sscanf( cBuf, "%s %f %f", &sSeit[ iNo ].cName, &sSeit[ iNo ].fMth, &sSeit[ iNo ].fEng ); printf( "%2d %-6s %5.1f %5.1f\n", iNo, sSeit[ iNo ].cName, sSeit[ iNo ].fMth, sSeit[ iNo ].fEng ); if( 3 != iRet ) Error( "入力項目数異常\n" ); if( NUMBER <= ++iNo ) Error( "お腹いっぱい\n" ); } fclose( fp ); return( iNo ); // 人数を返す ≠ 0 } void get_student_average( int iii ) { float fHeikin; fHeikin = ( sSeit[ iii ].fMth + sSeit[ iii ].fEng ) / 2.0; printf( "%-6sの平均点は %5.1f です\n", sSeit[ iii ].cName, fHeikin ); } void get_average( int iNo ) // iNo ≠ 0 { int i; float fMsum = 0.0, fEsum = 0.0; for( i = 0; i < iNo; i++ ){ fMsum += sSeit[ i ].fMth; fEsum += sSeit[ i ].fEng; } printf( "数学の平均点は %5.1f です\n", ( fMsum / (float)iNo ) ); printf( "英語の平均点は %5.1f です\n", ( fEsum / (float)iNo ) ); } void main() { int iNo, i; iNo = fset_all_student(); // 全員分を1回で読み込む for( i = 0; i < iNo; i++ ) get_student_average( i ); get_average( iNo ); } 注:インデントに全角空白を用いています。タブに一括変換して下さい。
お礼
ありがとうございました。 おかげで分かりました!!
- asuncion
- ベストアンサー率33% (2127/6289)
> ループ条件がnumberだと実行すると強制終了になってしまいます・・ 入力ファイル件数が100件未満の場合を考慮した コードを書かれていますか? > struct student *fset_all_student(void)の関数の > n=i(この場合は3) この場合は3、の意味がわかりません。 入力データの件数が3件なのですか? 名前、数学、英語の3項目の話とごっちゃになっていませんか? 入力ファイルの行数は何行ですか?
補足
また回答ありがとうございます。 ファイルの読み込みが 関数*fset_all_student(void)の while(fgets(&buffer[0],sizeof(buffer),fpin) !=NULL ) なのでi++で読み込んだ行数をn=iで記録してにして他の関数やmain関数でこのnの値を使いたいということです。そうすれば100件未満でも読み込むことができるので。 この場合3というのは入力ファイルが3行なので input.txt yuki 65 70 rika 78 90 eri 30 60 3と言う意味です。
- asuncion
- ベストアンサー率33% (2127/6289)
おっと失礼。 > ループの終了条件は i < number でないとまずいです。 ループの終了条件、ではなくて、ループの継続条件、でした。
- asuncion
- ベストアンサー率33% (2127/6289)
> for文のところのnを実数に変えても実行できません 最大100件までデータを扱えるのですから、 ループの終了条件は i < number でないとまずいです。 また、100件読まないうちに入力が終わることがあり得ますので、 それを考慮するコードでないとまずいです。 とりあえず、外部変数 n を使わなくてすむように、 設計変更してみてはいかがでしょう。
補足
また回答ありがとうございます ループ条件がnumberだと実行すると強制終了になってしまいます・・ struct student *fset_all_student(void)の関数の n=i(この場合は3)の値を他の関数やmain関数で扱いたいんですがどうすればいいですか?
- asuncion
- ベストアンサー率33% (2127/6289)
#1の者です。 コンパイルすると警告が出ますよね。 まず、その警告が出ないようにすることから始めてみてはいかがでしょうか。 なお、警告が出なくなったとしても、外部変数nの値がゼロであるため、 main関数における2つのfor文によるループは、どちらも実行しません。 その状態で平均値を求めようとするので、ゼロ割りか何かの エラーが出てしまうのではないでしょうか。
補足
回答ありがとうございます。 警告をなくしてもfor文のところのnを実数に変えても実行できません(泣)
- asuncion
- ベストアンサー率33% (2127/6289)
> コンパイルはできても実行するとエラーが出ます。 どんなエラーですか?
補足
ほんと回答ありがとうございます!! float GetMathAve(int Count) のstp = StudentTabe;が未定義のシンボル char *ExtractToken(char *cp, char *text) *cp = "\0";の移植性のないポインタ変換 とエラーがでてしまいます… この場合すればいいですか?