人生最悪の忘れ物

簡単な質問で恐縮ですが、現在WindowsでVC++2010Expressでコードを書いています。

そこで、以下の様な入力文字数を返すコードを書いているのですが、コマンドプロンプト上でCtrl+Z(Ctrl+Dも試した)を押してもストップしてくれません。(コマンドプロンプト上に「^Z(^D)」と表示されるだけです。

どなたか教えて頂けませんでしょうか?


#include <stdio.h>
int main()
{
int strText;
int counter=0;

while( (strText=getchar() ) != EOF){
++counter;
}
putchar(counter);

return 0;
}

A 回答 (14件中1~10件)

>私もそれが気になって「BackSpace」や「Delete」も試してみましたが、カウントされずに表示通りの文字数が返って来ました。


>この辺の動作が不明なのですが、「getchar()がEOF以外の値を返却した回数のカウントとなっているハズ」ではないのでしょうか?

標準入力は、通常では「バッファリング」されているので、改行がストリームを通過しないとプログラムに制御が戻りません。

「A」「B」「C」「バックスペース」「バックスペース」「^Z」「^Z」「1」「2」「3」と押しても、プログラムに返ってきません。

最後に「Enter」を押した瞬間、バッファ内の文字が整理されてから、プログラムに返って来ます。

上記の場合では、バッファ内が「A」「^Z」「^Z」「1」「2」「3」「\n」に整理されてから、プログラムに制御が戻ります。

「B」「C」「バックスペース2個」は、バッファが整理された時に削除されてしまいます。

すると、最初のgetcharには「A」が返されます。

その「A」は「EOFではない」ので、ループを繰り返します。

次にあるのは「^Z」ですが、これは「テキストモードでのファイルの終わり」なので、getcharは「ストリームが尽きた」と処理して「EOF」を返します。

この「EOF」は、通常は「文字コードに無い値」に定義されています(通常は「-1」になっている筈)

文字コードは「0~255」の筈ですから、どんな文字を入れても「-1」とは一致しません。

getcharが「EOF」を返したので、貴方のプログラムはループを終了してしまいます。

結果、上記のように入力すると、結果は「1」になるでしょう。

これは、プログラムを以下のようにすると「実感」出来ます。

#include <stdio.h>
int main()
{
int strText;
int counter=0;

while( (strText=getchar() ) != EOF){
++counter;
printf("L:%d\n",counter);
}
printf("E:%d\n",counter);

return 0;
}

プログラムが起動したら

ABC「Enter」DEF「Enter」123ABC「BS」「BS」「^Z」「Enter」と入力してみましょう。

結果は

ABC
L:1
L:2
L:3
L:4
DEF
L:5
L:6
L:7
L:8
123A^Z
L:9
L:10
L:11
L:12
E:12

となる筈です。

「Enter」を押さない限りはプログラムに戻って来ないので、「A」「B」「C」を入力した段階では、ループの中にある

printf("L:%d\n",counter);

は実行されません。

そして「Enter」を押した瞬間、それまでの間にバッファに溜まった文字が「改行も1文字」としてgetcharから返って来ます。

なので「Enter」を入力した瞬間、「A」「B」「C」「\n」が順に連続で返って来て、ループ内の表示が4回連続で一気に行われます。
    • good
    • 0
この回答へのお礼

わざわざ懇切丁寧に解説して頂いてありがとうございます!すごくわかりやすかったです。

ところで、ご示し頂いたコードをそのまま試してみたのですが、
不思議なことに、

(前略)
123A^Z
L:9
L:10
L:11
L:12
L:13

となり、もう一度「^Z」「Enter」を押さねば「E:13」という出力が現れません。
これは一体どういうことなんでしょうか??

お礼日時:2014/03/10 14:35

>こういうことってなかなか入門書なんかには書いてないので非常にありがたいです!



入門書レベルではそこまで深い(環境依存度の高い)ネタはやらないんでしょう。
# たとえば質問でも書かれているCtrl+DでEOFはUNIX系の場合(というかシェル?)依存ですし。

>本にはこうなりますってものと同じもの書いてもならないと、本が間違ってるのか何のか分からなくなりますからね。

著者が前提としている(あるいは動作確認した)環境が明示されていないモノは…避けた方がいいかもしれませんね。
たいていはOSやコンパイラ、バージョンなんかが明記されているかと。
最初の方に書かれているか、後ろの方の索引近くに書かれているかという違いはありますが。
# 入門書なり購入するときは明記されているか確認・納得の上で購入した方がよいでしょう。
    • good
    • 0

>ところで、ご示し頂いたコードをそのまま試してみたのですが、


>不思議なことに、
>(略)
>となり、もう一度「^Z」「Enter」を押さねば「E:13」という出力が現れません。
>これは一体どういうことなんでしょうか??

「^Z」の扱いについては、一部「環境依存」の部分があって、他の回答にある通り

「行頭での^ZのみEOFが返り、行頭ではない^Zは\x1aが返る」

と言う処理系と

「行頭でも、行頭以外でも^Zが現れた時点でEOFが返る」

と言う処理系があります。

なので、Visual Studioのデバッグ画面で試さず、リリース版exeを作ってからコマンドプロンプトで実行して試すと、結果が変わる可能性があります。

因みに、当方が試した環境は、後者の「^Zが現れた時点でEOFが返る」のタイプでした。
    • good
    • 1
この回答へのお礼

なるほど~。色々と環境の影響があるんですね。。
こういうことってなかなか入門書なんかには書いてないので非常にありがたいです!
本にはこうなりますってものと同じもの書いてもならないと、本が間違ってるのか何のか分からなくなりますからね。

お礼日時:2014/03/11 11:41

#7へのお礼の中の疑問


> もう一度「^Z」「Enter」を押さねば「E:13」という出力が現れません。
OSの仕様か、Visual Studioの仕様かわかりませんが、Ctrl+Zが入力行の先頭にないとEOF(値は-1)は返ってきません。行の途中に入れると値0x1aが返ってきます。また、Ctrl+Zに続けて文字を入力させようとしても、その行のCtrl+Zより後の文字は改行文字を含めて破棄されてしまいます。
    • good
    • 0

>その辺を制御したいならOS依存になるはずですがWindowsだとどうするんだろ。



そこまで行くとWindowsAPIでしょうか……。

http://msdn.microsoft.com/ja-jp/library/cc429744 …
なんての見つけましたけど……。

GetStdHandle()でハンドルを取得、GetConsoleMode()で現在のモードを取得して、
ENABLE_LINE_INPUTのフラグを落としてSetConsoleMode()で設定…でしょうか……。
APIで変更した内容がCランタイムライブラリの方に影響を与えるかどうか?に関しては疑問ですが。
# ランタイムの方の行バッファも無効にする必要がありますかねぇ…。


って、置いてけぼりとか言いつつ続けてしまう私。
# さすがに試してまではいない。
# コンソール上で動作するゲームでも作らないと行バッファとか問題にならない…でしょうし。

この回答への補足

途中から全然付いて行けてない質問主ですけど、どうぞお気になさらずプロの方同士で心ゆくまでディスカッションして下さい!

補足日時:2014/03/11 10:20
    • good
    • 0

バックスペースやデリートで編集できるのは、(Cライブラリの)標準入力の行バッファリングではなく、OS側の行バッファリングの話かと。


なので、その辺を制御したいならOS依存になるはずですがWindowsだとどうするんだろ。
    • good
    • 0

>それとも, VisualStudio の統合環境から?



でした。
というか面倒だったのでreturnにブレークポイント置いた。
まぁ本筋から逸れていってます……かね?

コンソール(CUI)使うことはほとんど無いしなぁ。
# 最近はC#使うことが多いし。

なんか質問者さん置いてけぼりにしている感が……。
    • good
    • 0

OS レベルでバッファリングされると, そうなっちゃいますね>#6. もともとそこは C言語レベルではど~しよ~もないわけで, 実行環境に依存した方法を使わざるを得ない. 例えば, Windows 環境なら conio.h とか?



あと, どうやってプログラムを実行していますか? コマンドプロンプトから実行してる? それとも, VisualStudio の統合環境から? はたまた, それら以外の何か?
    • good
    • 0

どうやらそのようで…>#5



#include <stdio.h>
int main()
{
 int strText;
 int counter=0;

 while( (strText=getchar() ) != EOF){
  ++counter;
 }
 printf("\nCount=%d\n", counter);

 setvbuf( stdin, NULL, _IONBF, BUFSIZ );
 counter=0;
 while( (strText=getchar() ) != EOF){
  ++counter;
 }
 printf("\nCount=%d\n", counter);

 return 0;
}

とか、書いて試してみましたが動作は変わらず。

http://support.microsoft.com/kb/45563/ja
コンソール相手だと効かない…ってことですかねぇ。
# リダイレクトは試していない。

そんな訳で、#4はちょっと的外れな回答となってしまいました。
    • good
    • 0
この回答へのお礼

いえいえ、とんでもない。試して頂いてありがとうございます!

お礼日時:2014/03/10 14:26

標準入力が行バッファリングになってると「バックスペース」とか「デリート」とかで修正できるので, 「押したキーの数」と「実際に読み込んだ『文字』の数」とは異なりますね.



標準入力が端末に割り当てられている場合は行バッファリングだったような気がする.
    • good
    • 0
この回答へのお礼

ありがとうございます。
好奇心からの質問ですが、標準入力を行バッファリングから文字単位でバッファリングする方法もあるのでしょうか?

お礼日時:2014/03/10 14:26

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

このQ&Aを見た人はこんなQ&Aも見ています


おすすめ情報