アプリ版:「スタンプのみでお礼する」機能のリリースについて

C言語でscanfの動作を確かめる為に、次のプログラムを作成して実行してみました。

#include <stdio.h>

int main(void){
int i;
printf("Input1:");
scanf("%d",&i);
printf("Input2:");
scanf("%d",&i);
printf("Output:%d",i);

return 0;
}


このプログラムは1回目と2回目に数値を入力すると正常に出力します。

例 1回目の入力に10、2回目の入力に20を入力した時の出力結果

Input1:10
Input2:20
Output:20


今度は1回目で数値、2回目で文字を入力してみました。
そしたら2回目の入力が無視されました。

例 1回目の入力に10、2回目の入力にAを入力した時の出力結果

Input1:10
Input2:A
Output:10


今度は1回目で文字を入力してみました。
そしたら2回目の入力は要求せず、いきなりよく分からない数値が出力されました。

例 1回目でAを入力した時の出力結果

Input1:A
Input2:Output:-858993460


どうして文字を入力したら正常に出力しなくなるのでしょうか?

A 回答 (10件)

少し前に私も回答した質問です。

ここで少し詳しいマニュアルを紹介しています。

http://oshiete.goo.ne.jp/qa/6721320.html

scanfって、正しく使えば便利だけど、失敗するととんでもない目にあう、という、C言語の特徴をそのまま象徴するような関数です。
一度、詳細なマニュアルや参考書をよく読んだ上で、まだわからなかったら質問するくらいでないと、理解できないかもしれません。


面倒なのでscanfを使わない、というのも有効な一つの解決策です。
    • good
    • 1
この回答へのお礼

有難うございました。参考にします。
ただ、そのサイトの「ヘッダファイル一覧」のscanfの部分をクリックしたら意味不明の文字列が並んでしまいました。
まだ未完成って事かな?

お礼日時:2011/05/30 18:35

「空白文字」というのは, isspace が真となる文字のこととなっています. その中には


' ', '\n', '\r', '\f', '\t', '\v'
は必ず含まれます.

と規格にちゃんと書いてあるので, 人に聞くだけでなく自分でも調べるといいと思うよ.
    • good
    • 1
この回答へのお礼

改行も空白文字に含まれているのですね。
一応ネットで調べたりはしているのですが、なかなか知りたい情報が見つけられなくて・・・

回答有難うございました。

お礼日時:2011/05/30 18:12

それは「scanf の仕様を確認しろ」で終わりなんだけど, ぶっちゃけていえば


一部の変換 (%c や %[ など) を除いて最初の空白文字は読み飛ばす
という仕様になっているからです. 例えば %d で読み込むときは「空白は読み飛ばす」ことになっています. ですが, %c で読み込むときには「空白も読む」ことになっています.

この回答への補足

%dの時は「空白文字は読み飛ばす」というのは分かりましたが、そうすると[ENTER]は空白と同様に扱われるという事でしょうか?
もしかして、[ENTER]以外の制御文字でも空白と同様に扱われるのでしょうか?

補足日時:2011/05/26 19:17
    • good
    • 0
この回答へのお礼

なるほど、[ENTER]を文字として扱わなかったのではなく、その部分を読み飛ばしたのですね。
回答有難うございました。

お礼日時:2011/05/26 19:08

> 1文字入力の時は、たとえ入力待ちの型が同じでも上手くいきませんでした。


> どうして1文字入力の時は上手くいかないのでしょうか?

うまくいってると思いますよ。
あなたは、

A[ENTER]

と入力したのでしょうから、最初のscanfで'A'が、次のscanfで[ENTER]が読みだされているはずです。

この回答への補足

[ENTER]も1文字とみなされる事は分かりましたが、新たな謎が生まれました。

質問した時に記述したソースコードで、1回目に数値と入力したら上手くいきます。
しかし、例えば1回目に10を入力したら標準入力は10[ENTER]になると思います。
そしたら1回目のscanfでiに10が格納されて、2回目のscanfで失敗してしまい次のように出力される気がします。

Input1:10
Input2:Output:10

どうして文字型の時は[ENTER]を文字として扱ったのに、整数型の時は[ENTER]を文字として扱わないのでしょうか?

補足日時:2011/05/25 22:20
    • good
    • 0
この回答へのお礼

[ENTER]という値が入力されているのですか。
これじゃあ文字で出力しても分かりませんね。

回答有難うございました。

お礼日時:2011/05/25 19:02

> どうして文字を入力したら正常に出力しなくなるのでしょうか?



理由はscanf()が正しく動いてないから。

scanf()関数は正しく変換できた値の数を返します。

例えば、
int i1, i2;
scanf( "%d %d", &i1, &i2 );
なら、2つの整数が正しく設定できた場合には2を返します。

もし、お望みの動作が一度目の入力で不正値が入力された場合には、それを読み飛ばして2回目に入力された値を出力するという動作ならそのような手順で処理をプログラムする必要があります。


#include <stdio.h>

int main(void){
int i;
printf("Input1:");
if( 0 == scanf("%d",&i) ) {
char dummy[1024];
scanf( "%s", dummy );
}
printf("Input2:");
if( 1 == scanf("%d",&i) ) {
printf("Output:%d\n",i);
} else {
printf( "2度目の入力値が正しく変換できませんでした。n" );
}

return 0;
}
    • good
    • 0
この回答へのお礼

なるほど、scanfの戻り値は変換に成功した数ですか。
エラー処理には戻り値を使えば良いのですね。
ソースコードまで記述して頂き有難うございました。

お礼日時:2011/05/25 18:43

>#2さん


>Cでは、明示的にしない限り、変数を初期化しない。

ブロック内の自動変数では、というただし書きが必要だったのではないかなぁ、
などと思ったりしてます。
    • good
    • 0
この回答へのお礼

「ブロック内の自動変数では、」ってことは、それ以外では明示的にしなくても良い場面があるって事ですね。
う~ん、一体どういう場面なら明示的にしなくて良いのだろう?

回答有難うございました。

お礼日時:2011/05/25 18:20

もちろん「バッファオーバーフローを回避する」こともできるんですけどね>#3.



「ちゃんと使えば使える, でも世の中には『ちゃんと使うことのできない』人が多いだけ」ということもできるかな.
    • good
    • 0
この回答へのお礼

scanfって意外に使いこなすのが難しいのですね。
回答有難うございました。

お礼日時:2011/05/25 18:13

scanfの仕様によるものです。



%dを指定しているのに"A"のように整数に変換できない文字列が入力された場合、scanfは読み取れたフィールドの数として0を戻り値として返し、変換できなかった文字列は標準入力に残したままにします。
そうすると、整数に変換できない文字列が標準入力に残ったままの状態で、次のscanfが実行されます。当然、変換できないで終了します。
2つのscanfは失敗しているので、iには何も代入されない状態となり、iの値を表示させても不定な値が表示されるだけです。

scanfを使わないようにといわれるのは、このような仕様がわかりずらいことや、%sで文字列を読み込もうとする場合に、入力しだいで文字列の長さが変わるため、用意した文字列サイズを超えてしまうバッファーオーバーランが起こりやすいためです。特に動作が不安定ということはありません。

この回答への補足

No.2さんとNo.3さんの回答を元に整理してみると、scanfの処理は次のように認識しましたが、この認識で合っているでしょうか?

「入力したデータを標準入力に送り、読み込みに成功するとファイルポインタを進ませて、2回目の読み込みでは次の文字から読み込みを行う。読み込みに失敗するとファイルポインタを進ませずに、2回目の読み込みでは前の文字が残った状態で読み込みを行う。」

補足日時:2011/05/25 18:08
    • good
    • 0
この回答へのお礼

読み込みに失敗すると文字列は標準入力に残ったままになるのですね。
scanfっていろんな問題点があるのですね。

回答有難うございました。

お礼日時:2011/05/25 18:01

scanfの仕様と、C言語の変数の初期値の関係です。



まず、そのプログラムはscanfでエラー処理を行なっていません。
そのため、正常に読めても、失敗しても、とにかく次へ進みます。

> Input1:10
> Input2:A
> Output:10

10がscanfでiに代入される
→「A」では「%d」に対応しないので失敗。
失敗なので、ファイルポインタは移動しない、iの値はもとのまま
→10が表示される

> Input1:A
> Input2:Output:-858993460

一つめのscanfで「A」では「%d」に対応しないので失敗。
失敗なので、ファイルポインタは移動しない、iの値はもとのまま
→2つめのscanfでも、読み込み位置が前のままなので、「A」から読み込もうとして「%d」に対応しないので失敗。
失敗なので、ファイルポインタは移動しない、iの値はもとのまま
→iが表示される。
Cでは、明示的にしない限り、変数を初期化しない。変数iに何が入っているかは不定。今回はたまたま-858993460だった

scanf系を使う場合は、エラー処理を忘れずに。

http://ja.wikipedia.org/wiki/Scanf
4.4 異常な入力が行われた時の処理
あたりも参考に

この回答への補足

今度は次のようにプログラミングして試してみました。
以下のソースコードはNo.1さんの補足にも記述したのですが、改めて記述します。

#include <stdio.h>

int main(void){
char i;
printf("Input1:");
scanf("%c",&i);
printf("Input2:");
scanf("%c",&i);
printf("Output:%c",i);

return 0;
}

ここで、1回目の入力をAとしてみました。
1回目のscanfで「A」は「%c」に対応するので読み込みに成功し、ファイルポインタは移動して再び入力待ちになるはずです。
しかし、実際は2回目の入力が省略されてしまいました。
どうして2回目の入力が省略されたのでしょうか?

補足日時:2011/05/25 08:28
    • good
    • 0
この回答へのお礼

なるほど、scanfはそうやって処理していたのですか。
scanfにはエラー処理が必要なんですね。

回答有難うございました。

お礼日時:2011/05/25 08:01

scanfの中で使われている「%d」は、整数の入力を待っている状態だからでしょう。


1文字入力したい場合は、scanf("%c", &i)、文字列ならscanf("%s", i)(このiは配列)
などとしないといけません。
それに付随したエラーかと。
また、scanf自体が不安定な命令だからかもしれません。
大学では「scanfはあまり使わないように」と言われました。

もし文字も数値も入力したのなら、文字列を入力する形式にして、
その後、数値に変換する、という手順を踏むのがよいかと思われます。

この回答への補足

今度は、試しに入力待ちの型を同じにしてみました。

#include <stdio.h>

int main(void){
char i;
printf("Input1:");
scanf("%c",&i);
printf("Input2:");
scanf("%c",&i);
printf("Output:%c",i);

return 0;
}

ここで、1回目の入力をAとした時、何故か2回目の入力は省略されて、出力結果は次のようになりました。

Input1:A
Input2:Output:

1文字入力の時は、たとえ入力待ちの型が同じでも上手くいきませんでした。
どうして1文字入力の時は上手くいかないのでしょうか?

補足日時:2011/05/25 07:17
    • good
    • 0
この回答へのお礼

入力待ちの型が違うと上手くいかないのですか。
scanfは多用しない方が良いのですね。

回答有難うございました。

お礼日時:2011/05/25 06:25

お探しのQ&Aが見つからない時は、教えて!gooで質問しましょう!