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

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

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

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

Q&A

解決済

4回答

1966閲覧

scanfで文字列と整数を読み込む

PC_breakman

総合スコア30

C

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

0グッド

0クリップ

投稿2020/05/29 11:52

前提・実現したいこと

C言語で以下のことをするプログラムを作成しました。
「任意数の人が異なる正の整数が書かれたくじを引き、くじの番号を比較して小さい人から1位、2位とし、その一位と二位の人の名前を出力する。」
[入力例]
yamada 2560
okada 195
ishida 4532
osaki 210
[出力例]
okada
osaki

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

下に張り付けたコードを実行すると、どの場合でも二番目に入力した人の名前が二つ続けて出力されてしまいます。 上の例でいえば、 okada okada と出てしまいます。 なぜこうなるのか、そのようにすれば解決できるのか見当がつかないので教えていただきたいです。 どうかよろしくお願いいたします。 コンパイルと実行はできます。

該当のソースコード

C

1#include <stdio.h> 2 3struct member{ //くじを引いた人とそのくじの番号をセットにするための構造体 4 char name[100]; 5 int lot; 6}; 7 8int main(void){ 9 struct member people[5000]; //くじを引いた人を格納する配列 10 int i = 0; //ループのための変数 11 while(1){ //入力終了までループ回す 12 scanf("%s %d",people[i].name,&people[i].lot); 13 if(scanf("%s %d",people[i].name,&people[i].lot) == EOF){ 14 break; 15 } 16 i++; 17 } 18 int min = 10000; 19 int j = 0; 20 for(j = 0; j < i; j++){ //くじの値が最小である配列の番号をminに代入 21 if(min > people[j].lot){ 22 min = j; 23 } 24 } 25 int secmin = 10001; 26 int k = 0; 27 for(k = 0; k < i; k++){ //くじの値が二番目に小さい配列の番号をsecminに代入 28 if(min < secmin && secmin > people[k].lot){ 29 secmin = k; 30 } 31 } 32 printf("%s\n",people[min].name); 33 printf("%s",people[secmin].name); //一位の人と二位の人の名前を出力 34 return 0; 35}

試したこと

nameとlotでscanfをわけたらだめなのかなと思ったので一緒にしてみたりしました。
またscanfの入力終了はEOFで表現できると知ったので使用しました。

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

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

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

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

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

guest

回答4

0

ベストアンサー

まず、入力部分ですが、

c

1 while(1){ //入力終了までループ回す 2 if(scanf("%s %d",people[i].name,&people[i].lot) == EOF){

のように scanf はループ毎に一回入力するようにします。

 あと、min と secmin には、くじの値を入れていくべきですが、people のインデックスで更新してしまっているのでおかしくなっています。インデックスは、別の変数で保存するようにすると良いと思います。

【追記】
上記の処置を加えてもまだ2番目の人を選択する条件がおかしいです。

c

1if(min < secmin && secmin > people[k].lot){

c

1if(min < people[k].lot && secmin > people[k].lot){

に修正し、最も小さい値と対象者のくじの値と比べるようにします。

投稿2020/05/29 12:19

編集2020/05/29 12:39
Yasumichi

総合スコア1773

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

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

PC_breakman

2020/05/29 12:32

#include <stdio.h> struct member{ char name[100]; int lot; }; int main(void){ struct member people[5000]; int i = 0; while(1){ if(scanf("%s %d",people[i].name,&people[i].lot) == EOF){ break; } i++; } int min = 10000; int minnum; //一位のインデックス保存用変数 int j = 0; for(j = 0; j < i; j++){ if(min > people[j].lot){ min = people[j].lot; minnum = j; } } int secmin = 10001; int secminnum; //二位のインデックス保存用変数 int k = 0; for(k = 0; k < i; k++){ if(min < secmin && secmin > people[k].lot){ secmin = people[k].lot; secminnum = k; } } printf("%s\n",people[minnum].name); printf("%s",people[secminnum].name); return 0; } 回答ありがとうございます。 ご指摘を受け、上記のように修正したのですが、今度は一位の人の名前が二回出力されるようになりました。一位の人のくじの値と二位の人のくじの値が同じになるはずはないのに、なぜこうなってしまうかわかりません。
Yasumichi

2020/05/29 12:40 編集

2番目のループの中での条件がおかしいです。 if(min < secmin && secmin > people[k].lot){ ↓ if(min < people[k].lot && secmin > people[k].lot){
PC_breakman

2020/05/29 12:40

解決いたしました。本当にありがとうございます。
Yasumichi

2020/05/29 13:17 編集

ちなみに secmin と secminnum の初期化を一つ目のループの前に移動し、一つめのループで min より小さい人が見つかった時点で前の値を secmin=min; secminnum=minnum; のように退避すれば、一つ目のループだけで終わります。  その場合、minnum と secnum は 5000 と 5001 ぐらいで初期化しておけば、良いでしょう。
PC_breakman

2020/05/29 15:22

ご指摘の通りつくり変えてfor文一個バージョンでも書くことができました。とても勉強になります。本当に感謝しています。ありがとうございます。
guest

0

nameとlotでscanfをわけたらだめなのかなと思った

なぜそう思ったのでしょう? 実際、そんなことはありません。
(scafが2行になったのはここの修正の仕方を間違ったのだと思いますが)

ついでに、お題の範囲なら配列も必要ありません。
以下のようにすれば、今どきのPCであれば20億人ぐらいは扱えます。
(「くじ」というのは同じ数は2つはでない、という理解をしましたがよろしいですか?)

#include <limits.h> #include <stdio.h> struct member { //くじを引いた人とそのくじの番号をセットにするための構造体 char name[100]; int lot; }; int main(void) { struct member peopleEntry, //入力用変数 peopleMin = {.lot = INT_MAX}, //最小候補。intの最大数で初期化しておく peopleSecmin = {.lot = INT_MAX}; //2位候補 while (1) { //入力終了までループ回す scanf("%s", peopleEntry.name); //一度EOFが設定されると以降scanfは全てEOF if (scanf("%d", &peopleEntry.lot) == EOF) { break; } if (peopleMin.lot > peopleEntry.lot) { //最小更新? peopleSecmin = peopleMin; //今までの最小が2位にランクダウン peopleMin = peopleEntry; //最小更新 } else if (peopleSecmin.lot > peopleEntry.lot) { //2位更新? peopleSecmin = peopleEntry; //2位更新 } } printf("%s\n", peopleMin.name); //一位の人と二位の人の名前を出力 printf("%s\n", peopleSecmin.name); return 0; }

質問があればどうぞ。

投稿2020/05/30 00:35

thkana

総合スコア7703

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

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

PC_breakman

2020/05/30 01:56

判定までループに入れてしまえば配列を用いる必要はないということですか?
thkana

2020/05/30 02:24

n番目までで最小と2番目だけがわかっていればn+1番目のデータまで入力されたときの最小と2番目はわかりますから過去全部のデータを取っておく必要はありません。
PC_breakman

2020/05/30 02:32

確かにそうですね。勉強になります。回答していただきありがとうございます。
PC_breakman

2020/05/30 05:38

ご指摘の通り配列を作らない形でプログラムを組み、正常に動作させることができました。また、INT_MAX関数というのも知らなかったので今後は使ってみようと思います。 回答いただき本当にありがとうございました。
thkana

2020/05/30 06:52

INT_MAXは関数ではありません。マクロ(数値の置き換え)です。 それほどは使う機会はないと思いますが...
PC_breakman

2020/05/30 06:54

そうなんですね、勉強になります。本当にありがとうございます。
guest

0

まず、(既に指摘がありますが)

scanf("%s %d",people[i].name,&people[i].lot); if(scanf("%s %d",people[i].name,&people[i].lot) == EOF){

一行ごとの読み込みとなります。(奇数行が捨てられる)

min = j;

これでは、該当のインデックス(j)は保持されますが、最小値ではありません。従って min は、最初に 0が入って、それ以降は更新される事はありません。(最初のokada の原因。
それは、次の secmin を求めるループも同様。

あとは考えてみましょう。

[追記]
おっとこっちも忘れてた。 (既に指摘が出ていたが、、、)

if(min < secmin && secmin > people[k].lot){

デバッガで追うとかして、途中経過をみるようにしましょう。
デバッガ使えなければ、printf()とかで途中経過を出力する。
そうしないと、何が起きているか分かりません。

投稿2020/05/29 12:29

編集2020/05/29 12:40
pepperleaf

総合スコア6385

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

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

PC_breakman

2020/05/29 12:41

解決いたしました。本当にありがとうございます。
guest

0

まず、scanfが2つあるので、1つ目のscanfを削除します。

また、最小値を探すとろろで、minに「最小値自体」を入れるのか「最小値のインデックス」を入れるのかが混乱しています。
インデックスと値を比べても無意味です。

投稿2020/05/29 12:23

otn

総合スコア85901

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

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

PC_breakman

2020/05/29 12:40

解決いたしました。本当にありがとうございます。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.35%

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

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

質問する

関連した質問