--------------------------------------
#include<stdio.h>
int main(void)
{
double dt=0.0,sum=0.0;
char ss[80];
int ret;
ret=scanf("%lf",&dt);
puts("");
if(ret!=1){
gets(ss);
printf("数値を入力してください\n");
puts("");
}
while(dt!=999){
sum=sum+dt;
ret=scanf("%lf",&dt);
puts("");
if(ret!=1){
gets(ss);
printf("数値を入力してください\n");
puts("");
}
}
printf("合計=%f\n",sum);
return 0;
}
--------------------------------------
以上のプログラムで、入力した数値の合計を出し「999」が入力されたら終了させ、数値以外が入力されたら、gets関数で読み捨て入力を続けていくということをしたいのですが、例えば、
◎1-----------
2
3
4
999
合計=9.000000
---------------
◎1のように数値のみだと正しく表示されます。
次に、
◎2--------------------
a
整数を入力してください
b
整数を入力してください
2
3
999
合計=5.000000
------------------------
◎2のように数値以外を先に入力し、その後に数値を入力しても正しく表示されます。
次に、
◎3-------------------
2
3
a
数値を入力してください
b
数値を入力してください
999
合計=11.000000
-----------------------
◎3のように数値を入力した後に、数値以外を入力したら正しく表示されません。
次に、
◎4--------------------
2
a
整数を入力してください
b
整数を入力してください
3
999
合計=9.000000
------------------------
◎4のように数値をまず入力しその後、数値以外を入力する。その後、数値を入力して終了させても、合計値が正しく表示されません。
まだ、バッファについて完全に理解していないということもあり、何故こうなってしまうのか分かりません。
教えていただけると嬉しいです。
No.10ベストアンサー
- 回答日時:
>「a」、「b」を入力した時点で、「3」はバッファ内でどうなっているのでしょうか?
バッファ内には何もありません。入力バッファはgets()で捨てられています。
>正常な入力が次に行われるまで、正常に入力された値はバッファ内に残っているということでしょうか?
バッファ内ではなく「dtそのもの」に「前回の値」が入ったままになっています。
実は、2も「正しく動いてないが、偶然、結果が正しい値と一致しただけ」です。
scanfは「書式と一致した入力を受け取ったら指定された変数に値を代入して、書式に一致しない入力を受け取った時点で処理を中断し、それまでに代入出来た個数を返す」と言う仕様になっています。
◎2は
double dt=0.0,sum=0.0;
の初期化で「dtが0.0になっている状態」で、最初の
ret=scanf("%lf",&dt);
の行で「a」が入力され「dtは変更されずに0.0のまま」で
if(ret!=1){
gets(ss);
printf("数値を入力してください\n");
puts("");
}
が実行され、バッファ「だけ」が消去されます。しかし「dtは前のまま」です。そして「aが入力され、dtが何になっているか判らないというのに」以下の
while(dt!=999){
sum=sum+dt;
の部分で「値が何なのか判らないdtをsumに足している」のです。
ですが「運良く、dtを0.0に初期化したまま」だったので、sumの値は変わりません。
◎3は
double dt=0.0,sum=0.0;
の初期化で「dtが0.0になっている状態」で、最初の
ret=scanf("%lf",&dt);
で「2」が入力され、dtは2.0になります。retは1になりますから
if(ret!=1){
gets(ss);
printf("数値を入力してください\n");
puts("");
}
は実行しません。そして
while(dt!=999){
sum=sum+dt;
でsumにdtが足され、sumが2.0になります。次に
ret=scanf("%lf",&dt);
で「3」が入力され、dtが3.0になり、retは1になります。次の
if(ret!=1){
gets(ss);
printf("数値を入力してください\n");
puts("");
}
は実行されません。ここでwhileループを繰り返しますから
while(dt!=999){
sum=sum+dt;
でsumにdtが足され、sumが5.0になります。次に
ret=scanf("%lf",&dt);
で「a」が入力され、dtは3.0のまま、変更されません。入力は失敗してますからretは0になりますので
if(ret!=1){
gets(ss);
printf("数値を入力してください\n");
puts("");
}
が実行されます。そして、そのままwhileループを繰り返し
while(dt!=999){
sum=sum+dt;
でsumにdtが足され、sumが8.0になります。「a」を入力したのでsumにdtを足してはいけないのに、足してしまっています。続いて「b」を入力した時も「dtが3.0のまま、sumにdtを足す」ので「sumが11.0」になります。
結果として「999を入力して終了させると、sumが11.0になっている」ことになります。
入力の前と後で、dtとsumの値が何になっているか、まとめてみます。
◎1の場合
入力「2」 入力前のdt「0.0」 入力前のsum「0.0」 入力後のdt「2.0」 dtを加算後のsum「2.0」
入力「3」 入力前のdt「2.0」 入力前のsum「2.0」 入力後のdt「3.0」 dtを加算後のsum「5.0」
入力「4」 入力前のdt「3.0」 入力前のsum「5.0」 入力後のdt「4.0」 dtを加算後のsum「9.0」
入力「999」 sum「9.0」を表示
◎2の場合
入力「a」 入力前のdt「0.0」 入力前のsum「0.0」 入力後のdt「0.0」 dtを加算後のsum「0.0」
入力「b」 入力前のdt「0.0」 入力前のsum「0.0」 入力後のdt「0.0」 dtを加算後のsum「0.0」
入力「2」 入力前のdt「0.0」 入力前のsum「0.0」 入力後のdt「2.0」 dtを加算後のsum「2.0」
入力「3」 入力前のdt「2.0」 入力前のsum「2.0」 入力後のdt「3.0」 dtを加算後のsum「5.0」
入力「999」 sum「5.0」を表示
「a」「b」入力時はsumにdtを加算してはいけないが「偶然、dtが0.0のまま」なので、影響が出なかった。影響は出なかったが、バグはバグ。
◎3の場合
入力「2」 入力前のdt「0.0」 入力前のsum「0.0」 入力後のdt「2.0」 dtを加算後のsum「2.0」
入力「3」 入力前のdt「2.0」 入力前のsum「2.0」 入力後のdt「3.0」 dtを加算後のsum「5.0」
入力「a」 入力前のdt「3.0」 入力前のsum「5.0」 入力後のdt「3.0」 dtを加算後のsum「8.0」
入力「b」 入力前のdt「3.0」 入力前のsum「8.0」 入力後のdt「3.0」 dtを加算後のsum「11.0」
入力「999」 sum「11.0」を表示
ここで「バグの影響」がモロに出ています。
◎4
入力「2」 入力前のdt「0.0」 入力前のsum「0.0」 入力後のdt「2.0」 dtを加算後のsum「2.0」
入力「a」 入力前のdt「2.0」 入力前のsum「2.0」 入力後のdt「2.0」 dtを加算後のsum「4.0」
入力「b」 入力前のdt「2.0」 入力前のsum「4.0」 入力後のdt「2.0」 dtを加算後のsum「6.0」
入力「3」 入力前のdt「2.0」 入力前のsum「6.0」 入力後のdt「3.0」 dtを加算後のsum「9.0」
入力「999」 sum「9.0」を表示
ここも「バグの影響」がモロに出ています。
結論は「数値以外を入力した時も、sumにdtを足しているのが悪い」のです。
バッファがどうとか、そういう問題ではありません。
以下のように修正しましょう。
#include<stdio.h>
int main(void)
{
double dt=0.0,sum=0.0;
char ss[80];
int ret;
ret=scanf("%lf",&dt);
puts("");
if(ret==1){
sum=sum+dt; //入力が正常な時だけsumに足す
} else if(ret!=EOF){
gets(ss);
printf("数値を入力してください\n");
puts("");
}
while((ret!=EOF)&&(dt!=999)){
ret=scanf("%lf",&dt);
puts("");
if(ret==1){
sum=sum+dt; //入力が正常な時だけsumに足す
} else if(ret!=EOF){
gets(ss);
printf("数値を入力してください\n");
puts("");
}
}
printf("合計=%f\n",sum);
return 0;
}
あと、蛇足ですが「数字の後にEOF(Ctrl+Z)を入力したら、プログラムが永久に止まらなくなる」と言うのも修正してあります。
修正前のプログラムは
2<Enter>
3<Ctrl+Z><Enter>
と入力したら「止まらなくなる」ので絶対にやってはいけません。
ご回答ありがとうございます。
各実行結果のdtの値とsumの値を詳細にご回答していただき、かなり理解できました!
修正してもらったプログラムも理解できました!
修正してもらったプログラムで、
------------------------------------------
if(ret= =1){
sum=sum+dt; //入力が正常な時だけsumに足す
} else if(ret!=EOF){
gets(ss);
printf("数値を入力してください\n");
puts("");
}
------------------------------------------
最初のifでretで「1」を判定しているので、次のelse ifで「ret= =0」としても、問題ないですかね?
後、入力で「45abcd」とした場合、%lfで「45」だけを読み取り、バッファに残された「abcd」の判定の時点でエラー表示する。というのは、「45abcd」と入力した時点で、「45」を読み込まず、エラー表示するということは出来ないのですかね?
お答えいただければ嬉しいです。
No.15
- 回答日時:
>実際に業務で動くCプログラムを書いたことがないもので、
>間違ったことを書いてしまいました。
とはいうものの、今回の場合、
インタラクティブな環境で動けばじゅうぶん
(ファイルをリダイレクトすることはあまり想定してなさそう)かな、
という感じがしますので、
rewind(stdin);
で動くんだったらそれでもいいんじゃないかとも思います。
No.14
- 回答日時:
>後、入力で「45abcd」とした場合、%lfで「45」だけを読み取り、
>バッファに残された「abcd」の判定の時点でエラー表示する。
>というのは、「45abcd」と入力した時点で、「45」を読み込まず、
>エラー表示するということは出来ないのですかね?
できますよ。
scanfが1を返して来たら(retが1になっていたら)、次の1文字をgetchar()で拾ってみて、拾った文字が「'\n'またはEOF」ならOK、そうじゃないなら、続きに変な文字がある、って判ります。
入力が「45abcd」なら「scanfが1を返して、その後でgetcharすると、getcharが'a'を返します。
その場合は「scanfが1じゃない値を返した時」と同じ処理をしましょう。
なお「12 34」と入力した場合、1回目のscanfは「12」を受け取り、2回目のscanfは「キーボード入力をしてないのにも関わらず」続きの「34」を受け取ってしまいます。
つまり「1行の入力で、2回以上のscanfが行われてしまう」ので、注意が必要です。
例えば「1 2 3 4 999」と「1行で、スペースで区切って入力」すると、一気に計算して「合計=10.000000」って表示が出て終了してしまいます。
これも、前述の「scanfが1を返して来たら(retが1になっていたら)、次の1文字をgetchar()で拾ってみて、拾った文字が「'\n'またはEOF」ならOK、そうじゃないなら、続きに何かの文字がある」と言う判定で回避できます。
No.13
- 回答日時:
>今の時代、誰も「入力バッファを消去するつもりで、
>rewind(stdin)と書いてはいけない」って、教えてくれないんですねえ。
実際に業務で動くCプログラムを書いたことがないもので、
間違ったことを書いてしまいました。
No.12
- 回答日時:
蛇足な追記。
>まあおそらく、
>rewind(stdin);
>でじゅうぶんではないかと思いますけれど。
充分ではありません。重大なバグを生みます。
>お言葉ですが、rewindはれっきとした標準関数に属します。
確かに「標準関数」に属し「rewind(stdin)」の動作も定義されています。
>今回の仕様を満たすために使用することは、全く問題ありません。
問題大有りです。
「rewind(stdin)」は「標準入力ストリームのファイルポインタを先頭に戻す」と定義されています。
誰も「入力バッファを消去するつもりで、rewind(stdin)と書いた時、標準入力がリダイレクトされていた時の動作」を試してないんでしょうか?
1<改行>
2<改行>
a<改行>
b<改行>
<EOF>
と書かれたテキストファイルを、バッファを消去するつもりで、rewind(stdin)と書いたプログラムに入力リダイレクトしたら、どうなると思います?
「1」をscanfして、「2」をscanfして、「a」をscanfした瞬間、rewind(stdin)され、ファイルポインタが1行目に戻ります。
ファイルポインタが1行目に戻れば、「1」をscanfして、「2」をscanfして、「a」をscanfした瞬間、rewind(stdin)され、ファイルポインタが1行目に戻ります。
ファイルポインタが1行目に戻れば、「1」をscanfして、「2」をscanfして、「a」をscanfした瞬間、rewind(stdin)され、ファイルポインタが1行目に戻ります。
ファイルポインタが1行目に戻れば、「1」をscanfして、「2」をscanfして、「a」をscanfした瞬間、rewind(stdin)され、ファイルポインタが1行目に戻ります。
ファイルポインタが1行目に戻れば、「1」をscanfして、「2」をscanfして、「a」をscanfした瞬間、rewind(stdin)され、ファイルポインタが1行目に戻ります。
以下、永久に繰り返し。
今の時代、誰も「入力バッファを消去するつもりで、rewind(stdin)と書いてはいけない」って、教えてくれないんですねえ。困ったもんだ。
No.11
- 回答日時:
うっかりミス。
#include<stdio.h>
int main(void)
{
double dt=0.0,sum=0.0;
char ss[80];
int ret;
ret=scanf("%lf",&dt);
puts("");
if(ret==1){
if(dt!=999) sum=sum+dt; //入力が正常で999でない時だけsumに足す
} else if(ret!=EOF){
gets(ss);
printf("数値を入力してください\n");
puts("");
}
while((ret!=EOF)&&(dt!=999)){
ret=scanf("%lf",&dt);
puts("");
if(ret==1){
if(dt!=999) sum=sum+dt; //入力が正常で999でない時だけsumに足す
} else if(ret!=EOF){
gets(ss);
printf("数値を入力してください\n");
puts("");
}
}
printf("合計=%f\n",sum);
return 0;
}
999を入力した時はsumに足しちゃ駄目ですね。ウッカリミスです。
No.9
- 回答日時:
2を入力 dt:2.0 sum:0.0
whileループ開始
sum = 2.0 ... sum = 0.0 + 2.0 // ここの加算は想定通り
3の入力で dtが3.0に更新
if文の条件不成立のためループの先頭へ
sum = 5.0 ... sum = 2.0 + 3.0 // ここの加算は想定通り
aの入力 dtは3.0のまま
if文の条件成立のため文字列出力等の処理
ループの先頭へ
sum = 8.0 ... sum = 5.0 + 3.0 // ここの加算は想定外
bの入力dtは3.0のまま
if文の条件成立のため文字列出力等の処理
ループの先頭へ
sum = 11.0 ... sum 8.0 + 3.0 // ここの加算は想定外
999の入力 dtが999.0に更新
if文の条件不成立のためループの先頭へ
while文が不成立のためループを抜ける
つまりscanfが失敗した場合は 引数で与えられた変数への代入は行われないため 前回成功した値のままである点が考慮されていないのです
scanfに残ったバッファとは直接関係のない部分でバグっていますよ
while( dt != 999.0 ) {
sum += dt;
dt = 0.0;
ret = scanf( "%lf", %dt )
といった具合にしてもいいでしょう
ご回答ありがとうございます。
>aの入力 dtは3.0のまま
>bの入力dtは3.0のまま
>つまりscanfが失敗した場合は 引数で与えられた変数への代入は行われない
>ため 前回成功した値のままである点が考慮されていないのです
以上の内容理解できました!
デバッグのステップオーバーという機能を勉強して、まだ完璧ではないですが、リアルタイムでdt等の値を見て理解が深まりました。
お探しのQ&Aが見つからない時は、教えて!gooで質問しましょう!
似たような質問が見つかりました
- C言語・C++・C# C言語階乗の総和を求める 2 2023/03/04 23:31
- C言語・C++・C# 至急教えてください。プログラミングの問題です。 最初に正の整数nの入力を受け付け、次に分数の分子と分 1 2022/07/19 17:03
- C言語・C++・C# 至急教えてください。プログラミングの問題です。 malloc関数を使ってください!お願いします! 最 1 2022/07/21 09:28
- C言語・C++・C# 至急お願いします。プログラミングの問題です。 最初に正の整数nの入力を受け付け、次に分数の分子と分母 3 2022/07/19 17:09
- C言語・C++・C# C言語 3 2022/10/04 15:07
- C言語・C++・C# このプログラミングの問題を教えてほしいです。 キーボードからデータ数nとn個のデータを入力し、平均値 3 2022/12/19 22:51
- C言語・C++・C# 至急教えてください!プログラミングの問題です。 割られる整数と割る整数を受け取って、商と余りを出力す 3 2022/07/05 10:23
- C言語・C++・C# C言語 プログラミング 4 2022/05/22 11:53
- Java Java 年数計算 3 2023/01/28 10:52
- C言語・C++・C# c言語の問題です 3 2023/01/10 16:15
関連するカテゴリからQ&Aを探す
おすすめ情報
デイリーランキングこのカテゴリの人気デイリーQ&Aランキング
-
*をユーザーが入力した数字の数...
-
数字以外が入力されたらエラー...
-
正負を反転させて出力するプロ...
-
java初心者です。入力されたの...
-
Eclipseコンソール表示を、リセ...
-
scanf が無視されます
-
プログラミング初心者です。 Py...
-
C言語初心者です。ファイルの読...
-
VisualStudio2019のコードアナ...
-
入力値が1以下、かつ数字以外の...
-
Linuxで入力待ちなしkeyread関...
-
scanfが2回使えない・・・?;
-
enterでループ終了
-
キーボードを押すまで処理を中...
-
C言語 for文をつかって記号を表...
-
C言語・YesNo入力のループで解...
-
IF文で戻ることはできますか?...
-
あるキーを押したら強制終了さ...
-
2進数の1の数を数える問題
-
コマンドプロンプトからのEOFの...
マンスリーランキングこのカテゴリの人気マンスリーQ&Aランキング
-
*をユーザーが入力した数字の数...
-
正負を反転させて出力するプロ...
-
数字以外が入力されたらエラー...
-
プログラミング初心者です。 Py...
-
double型が正常に認識されてい...
-
java初心者です。入力されたの...
-
Eclipseコンソール表示を、リセ...
-
scanfが2回使えない・・・?;
-
C言語scanf_sで何故か2回入力に...
-
if文の条件にscanf関数を使うと…?
-
Linuxで入力待ちなしkeyread関...
-
プログラミングの問題です 「金...
-
ワードで文字を入力する時の変...
-
cout関数を使っているのですが...
-
batプログラム上で文字列を入力...
-
Userformの入力順序をタブオー...
-
scanf が無視されます
-
C言語 逆ピラミッドの作り方
-
gets_sがうまく動かない
-
Excel VBAで、Application.Inpu...
おすすめ情報