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

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

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

C言語は、1972年にAT&Tベル研究所の、デニス・リッチーが主体となって作成したプログラミング言語です。 B言語の後継言語として開発されたことからC言語と命名。そのため、表記法などはB言語やALGOLに近いとされています。 Cの拡張版であるC++言語とともに、現在世界中でもっとも普及されているプログラミング言語です。

プログラミング言語

プログラミング言語はパソコン上で実行することができるソースコードを記述する為に扱う言語の総称です。

コードレビュー

コードレビューは、ソフトウェア開発の一工程で、 ソースコードの検査を行い、開発工程で見過ごされた誤りを検出する事で、 ソフトウェア品質を高めるためのものです。

配列

配列は、各データの要素(値または変数)が連続的に並べられたデータ構造です。各配列は添え字(INDEX)で識別されています。

Q&A

解決済

3回答

3447閲覧

C言語で簡易成績処理システムの作成<配列構造と乱数の扱いに関する質問>

SugarNote123

総合スコア2

C

C言語は、1972年にAT&Tベル研究所の、デニス・リッチーが主体となって作成したプログラミング言語です。 B言語の後継言語として開発されたことからC言語と命名。そのため、表記法などはB言語やALGOLに近いとされています。 Cの拡張版であるC++言語とともに、現在世界中でもっとも普及されているプログラミング言語です。

プログラミング言語

プログラミング言語はパソコン上で実行することができるソースコードを記述する為に扱う言語の総称です。

コードレビュー

コードレビューは、ソフトウェア開発の一工程で、 ソースコードの検査を行い、開発工程で見過ごされた誤りを検出する事で、 ソフトウェア品質を高めるためのものです。

配列

配列は、各データの要素(値または変数)が連続的に並べられたデータ構造です。各配列は添え字(INDEX)で識別されています。

0グッド

0クリップ

投稿2020/07/24 19:43

前提・実現したいこと

大学のC言語の自由課題で、簡易成績処理システムを作成しています。
<仕様>
・成績処理を行う人数を入力し、学生氏名と得点を人数分入力する。
・入力された数値を元に、平均得点、最高得点、最小得点、最高得点者の人数および氏名を出力する。
・なお、成績処理を行う人数を入力後、学生氏名と得点の入力方法を手動と自動(シミュレーター)で選択できるようにする。
・シミュレーターは乱数を取得し、その乱数に従い予め配列に格納された複数の氏名から一つを洗濯して、構造体内の変数に格納する。
・得点も同様に、乱数を用いて0~100の間で取得し構造体内の変数に格納する。

発生している問題・エラーメッセージ

コンパイル時のエラーは特にありません。
発生している問題は以下の2点です。
1.手動入力の場合は問題なく動作するのですが、自動入力にした場合、以下のように全ての構造体配列に同じ氏名と得点が格納されてしまいます。

人数を入力してください。人数=3 成績を入力します。 手動入力の場合は「m」をを入力してください。 自動入力(シミュレーション)する場合は「a」を入力してください。 a <結果一覧> Student_1_清水の点数は89 Student_2_清水の点数は89 Student_3_清水の点数は89 平均は89.000000である。 最大値は89.000000である。 最小値は89.000000である。 最高得点者の人数は3人です。 最高得点者は以下の通りです。 Student_1_清水_89.000000点 Student_2_清水_89.000000点 Student_3_清水_89.000000点

ちなみに手動入力を選択した場合は以下のようになります。

人数を入力してください。人数=3 成績を入力します。 手動入力の場合は「m」をを入力してください。 自動入力(シミュレーション)する場合は「a」を入力してください。 m Student_1_Name=佐藤 Score=20 Student_2_Name=鈴木 Score=40 Student_3_Name=山口 Score=40 <結果一覧> Student_1_佐藤の点数は20 Student_2_鈴木の点数は40 Student_3_山口の点数は40 平均は33.333332である。 最大値は40.000000である。 最小値は20.000000である。 最高得点者の人数は2人です。 最高得点者は以下の通りです。 Student_2_鈴木_40.000000点 Student_3_山口_40.000000点

2.また、「佐々木」のみ3文字のため配列の大きさが均一ではなく、そのせいか、シミュレーターとして動作させた時に、氏名に「佐々木山口」と格納されてしまう事態が発生します。20個の氏名は日本の苗字ランキング上位20位を使用しており、特段意味があるわけでは無いので、全て2文字漢字の苗字に統一すれば一応、解決にはなるのですが、原因不明のまま変えてしまうのはモヤモヤするので、原因を追求したいです。

該当のソースコード

#include <stdio.h> #include <stdlib.h> #include <string.h> #include <time.h> #define N 1000 //構造体の定義 struct Seiseki{ char*pname;//名前を格納する(ポインタ) int score;//得点を格納する }; //プロトタイプ宣言 struct Seiseki input_Manual(int); struct Seiseki input_Auto(void); float calc_average(int*,struct Seiseki*); float calc_max(int*,struct Seiseki*); float calc_min(int*,struct Seiseki*); void output_students(int*,struct Seiseki*); void output_str(char*,float); void output_max(float,struct Seiseki*); //グローバル変数 int highest;//最高得点者の人数を格納 int highest_number[N];//最高得点者の番号を格納 //main関数 int main(){ int number;//名前を格納する人数を格納する変数 int*pnumber;//numberのアドレス(ポインタ) pnumber=&number; float ave;//平均を格納 float max;//最大値を格納 float min;//最小値を格納 struct Seiseki seiseki[N]; int i; char str[3]; char*pstr; pstr=str; char*pm="m"; char*pa="a"; printf("人数を入力してください。人数="); scanf("%d",pnumber); printf("成績を入力します。\n"); printf("手動入力の場合は「m」をを入力してください。\n"); printf("自動入力(シミュレーション)する場合は「a」を入力してください。\n"); scanf("%s",str); pstr=(char*)malloc(sizeof(char)*(strlen(str)+1)); strcpy(pstr,str); if(strcmp(pstr,pm)==0){//手動入力の場合の処理 for(i=0;i<*pnumber;i++){ seiseki[i]=input_Manual(i); if(seiseki[i].score>=101||seiseki[i].score<0){ printf("入力エラー。Scoreは0~100の間で入力してください。\n"); i=i-1; }; }; }else if(strcmp(pstr,pa)==0){//自動入力の場合の処理 for(i=0;i<*pnumber;i++){ seiseki[i]=input_Auto(); }; }else{ printf("入力エラー。プログラムを終了します。"); return 0; }; ave=calc_average(pnumber,seiseki); max=calc_max(pnumber,seiseki); min=calc_min(pnumber,seiseki); printf("<結果一覧>\n"); output_students(pnumber,seiseki); output_str("平均",ave); output_str("最大値",max); output_str("最小値",min); output_max(max,seiseki); return 0; } //input_Manual関数 struct Seiseki input_Manual(int i){ struct Seiseki temp; char str[100]; printf("Student_%d_Name=",i+1); scanf("%s",str); temp.pname=(char*)malloc(sizeof(char)*(strlen(str)+1)); strcpy(temp.pname,str); printf("Score="); scanf("%d",&temp.score); return temp; } //input_Auto関数 struct Seiseki input_Auto(){ struct Seiseki temp; int name=0,point=0; char str[20][9]={"佐藤","鈴木","高橋","田中","伊藤","渡辺","山本","中村","小林","加藤", "吉田","山田","佐々木","山口","松本","井上","木村","林","斎藤","清水"}; srand(time(NULL)); name=rand()%20;/*乱数を取得*/ temp.pname=(char*)malloc(sizeof(char)*(strlen(str[name])+1)); strcpy(temp.pname,str[name]); point=(rand()%101); temp.score=point; return temp; } //calc_average関数(平均得点の計算) float calc_average(int*pnumber,struct Seiseki*seiseki){ int i,j; float sum,ave; for(i=0;i<*pnumber;i++){ sum=sum+(seiseki[i].score); }; j=*pnumber; ave=sum/(float)j; return ave; } //calc_max関数(最高得点の計算と最高得点者の記録) float calc_max(int*pnumber,struct Seiseki*seiseki){ int i; float max=0; for(i=0; i<*pnumber; i++){ if(max<(seiseki[i]).score){ max=(seiseki[i].score); highest=1; highest_number[highest]=i+1; } else if(max==(seiseki[i].score)){ highest=highest+1; highest_number[highest]=i+1; } }; return max; } //calc_min関数(最小得点を計算) float calc_min(int*pnumber,struct Seiseki*seiseki){ int i; float min=100; for(i=0; i<*pnumber; i++){ if(min>=(seiseki[i].score)){ min=(seiseki[i].score); } }; return min; } //output_students関数(学生ごとの点数を表示する関数) void output_students(int*pnumber,struct Seiseki*seiseki){ int i; for(i=0;i<*pnumber;i++){ printf("Student_%d_%sの点数は%d\n",i+1,(seiseki[i].pname),(seiseki[i].score)); }; } //output_str関数(何らかの実数を文字列とセットに表示する関数) void output_str(char*moziretu,float number){ printf("%sは%fである。\n",moziretu,number); } //output_max関数(最高得点者の人数と氏名を表示する関数) void output_max(float max,struct Seiseki*seiseki){ int i; printf("最高得点者の人数は%d人です。\n",highest); printf("最高得点者は以下の通りです。\n"); for(i=0;i<highest;i++){ printf("Student_%d_",highest_number[i+1]); printf("%s",(seiseki[highest_number[i+1]-1].pname)); printf("_%f点\n",max); }; }

試したこと

ここに問題に対して試したことを記載してください。

補足情報(FW/ツールのバージョンなど)

ここにより詳細な情報を記載してください。

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

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

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

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

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

guest

回答3

0

srand()を呼ぶ位置がよろしくありません。

C

1 srand(time(NULL)); 2 name=rand()%20;/*乱数を取得*/

(このプログラムの場合、)時刻がほとんど変わらないうちにsrandを呼び出し、その直後にrandを呼んでいるので、得られる乱数は(ほぼ)いつも同一の値になってしまいます。
(修正する前に、nameの値をprintfでもしてみて確認しましょう)
ここのsrandの呼び出しは削除し、main()の頭あたりで1回だけ呼び出すようにすれば期待するような結果となるでしょう。

投稿2020/07/24 20:01

e-watt

総合スコア84

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

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

SugarNote123

2020/07/25 03:31

一つ目の疑問点については解決しました。srandの呼び出し位置に注意し、他の方のアンサーも参考に修正できました。ありがとうございます。高評価させていただきます。
guest

0

ベストアンサー

Seed が同じ状態で rand() を呼んでるから乱数パターンの最初の数字が返ってきて同じ値になってるんだと思います。この用途なら、srand() は最初に一回だけ呼べばいいと思います。

以下を実行してみるとわかると思います。

c

1#include <stdio.h> 2 3int main() { 4 printf("こうするとよい\n"); 5 srand(time(NULL)); 6 7 for (int i = 0; i < 10; i++) 8 { 9 int r = rand(); 10 printf("random: %d\n", r); 11 } 12 13 printf("こうすると同じになってしまう\n"); 14 for (int i = 0; i < 10; i++) 15 { 16 int s = time(NULL); 17 srand(s); 18 int r = rand(); 19 printf("random: %d, seed: %d\n", r, s); 20 } 21 return 0; 22}

投稿2020/07/24 19:59

bboydaisuke

総合スコア5308

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

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

SugarNote123

2020/07/25 03:30

一つ目の疑問点に関しては解決しました。ありがとうございます。
guest

0

C の文字列は「ナル文字」(ヌル文字とも言う)すなわち整数値 0 で終端する事はご存じかと思います。

かつて、C の処理系の多くは、日本語文字の1文字を2バイトで扱っていたので(Shift_JIS など)、質問者様のプログラムが意図通りに動く事が多かったのですが、現在、日本語処理の主流は Unicode になっており、1文字が3バイトになる事があります。

まずは、次の短いプログラムを実行してみてください。

#include <stdio.h> int main( void ) { char str[20][9]={"佐藤","鈴木","高橋","田中","伊藤","渡辺","山本","中村","小林","加藤", "吉田","山田","佐々木","山口","松本","井上","木村","林","斎藤","清水"}; int i; printf( "str[12]=%s\n", str[12] ); for ( i = 0; i < 9; i ++ ) { printf( "%d ", str[12][i] ); } printf( "\n" ); printf( "str[13]=%s\n", str[13] ); for ( i = 0; i < 9; i ++ ) { printf( "%d ", str[13][i] ); } return 0; }

「2つめの問題」が再現されたと思います。

このプログラムの実行結果を見ると、str[13] の "山口" は、7バイト目に 0 が出現しています。
対して str[12] の "佐々木" には 0 がありません。
という事は、この処理系における日本語文字の1文字は3バイトである事が強く推定されるのです。

次に、上記プログラムの 9 を 10 に変えてみます。

#include <stdio.h> int main( void ) { char str[20][10]={"佐藤","鈴木","高橋","田中","伊藤","渡辺","山本","中村","小林","加藤", "吉田","山田","佐々木","山口","松本","井上","木村","林","斎藤","清水"}; int i; printf( "str[12]=%s\n", str[12] ); for ( i = 0; i < 10; i ++ ) { printf( "%d ", str[12][i] ); } printf( "\n" ); printf( "str[13]=%s\n", str[13] ); for ( i = 0; i < 10; i ++ ) { printf( "%d ", str[13][i] ); } return 0; }

これで問題は解消したはずです。

このプログラムの実行結果を見ると、str[13] の "山口" は、先ほどと同じく7バイト目に 0 が出現しています。
対して str[12] の "佐々木" は10バイト目に 0 が出現しています。
これらの 0 によって、それぞれの文字列はきちんと終端し、意図通りの処理結果が得られるのです。

C で文字列を扱う際には、「終端のナル文字の分だけ、1バイト余計に確保しておかなければならない」……これは鉄則です

最初のプログラム(と、質問者様のプログラム)では、配列の1要素が9バイトしかなく、"佐々木" を終端するナル文字が格納されないために、"佐々木","山口" としたつもりが、メモリ上では "佐々木山口" になってしまっていたのです。

投稿2020/12/27 23:32

kurai

総合スコア85

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

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

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.35%

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

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

質問する

関連した質問