C言語関係掲示板

過去ログ

No.153.getchar()関数に関して


No.986

getchar() 関数に関係した質問です
投稿者---めい助(2002/01/29 21:00:00)


こんばんは。 二回目の投稿になります、”めいすけ”です。
今回は、getchar関数についての質問です。

以下のコードは、とあるC言語の入門本に記載されていたものです。
getchar( )関数の要求により、何らかの文字列を入力したさい、その
文字列と、その下にピリオド "." を表示させるものですが、何故そうなるのか? がわからずに悩んでいます。

#include<stdio.h>

int main(void)
{
char ch; /* 私なりの解釈です */
do {
ch = getchar (); /* この行で文字列の入力を要求 */
putchar('.'); /* ここでは、ピリオドの出力をしているが、では、変数 ch に取り込まれたであろう、文字列の内容はどのように出力されるのか? */
while(ch != '\n'); /* 改行文字にたどり着くまで繰り返し */
}

return 0;
}

getchar関数が何文字入力しようと、最初の一文字しか出力されないのはわかってはいるのですが、このコードを実行させると全ての文字列が表示されます。
例えば、puts(ch) とでもあれば、文字列を表示させる事は容易に想像がつくの
ですが、そんな記述もないので何故そうなるのかわからず困っています。
どなたかお教え頂けますでしょうか?


No.989

Re:getchar() 関数に関係した質問です
投稿者---kikk(2002/01/30 03:12:14)


ども。


>以下のコードは、とあるC言語の入門本に記載されていたものです。
>getchar( )関数の要求により、何らかの文字列を入力したさい、その
>文字列と、その下にピリオド "." を表示させるものですが、何故そうなるのか? がわからずに悩んでいます。

たしかに見た限りでは出力処理がありませんね。おそらく、「その文字列
と〜表示させるもの」というのは、入力時に表示されるものを指している
のではないでしょうか。その本が間違っている可能性もなくはないですが。
なお、標準関数の範囲では、入力時の入力した文字そのものの出力(エコー
バック)をとめることはできません。

提示されたコードでは、入力文字列の長さ+1個のピリオドが出力されます。
# while();のあたりでコンパイルエラーがでるので適当に修正したとして


>getchar関数が何文字入力しようと、最初の一文字しか出力されないのはわかってはいるのですが、このコードを実行させると全ての文字列が表示されます。

標準入力が入力を受け付けているときに、文字(列)が(改行によって)入力
されると、その内容は改行を含めていったんバッファにたくわえられます。
このバッファは標準入力が開かれるときに用意されるものです。getchar()は
標準入力から1文字取ってくる関数なので、結局、そのバッファ中のデータの
最初の1文字を取ってくることになります。提示されたコードでは、読み込んだ
文字が改行でないあいだループするようになっています。ループ中では
ピリオドの出力を行っているので、上記のような結果が得られます。


>例えば、puts(ch) とでもあれば、文字列を表示させる事は容易に想像がつくの

chは配列ではなく、また、読み込み関数getchar()も文字列単位で読むもの
ではないので、chには入力全体を保持することはできません。また、同様の
理由により、puts(ch)というのもできません。


余談ですが、参考までに。
改行なしで1文字読み込む非標準な関数にgetche()とgetch()というのが、
conio.hかcurses.hにあります。この2つ関数の違いは、入力した文字を表示
するかしないかです。詳細はライブラリリファレンスを参照してください。


では。

No.996

Re:getchar() 関数に関係した質問です
投稿者---めい助(2002/02/01 22:51:12)


>たしかに見た限りでは出力処理がありませんね。おそらく、「その文字列
>と〜表示させるもの」というのは、入力時に表示されるものを指している
>のではないでしょうか。

kikk さんご指摘の通り、文字列まで一緒に出力されるのではなく、単に
ピリオドが文字数 + 改行文字分 だけ出力されるものでした。
僕の勘違いでした。

所で新たな疑問が沸いてきました。

>標準入力が入力を受け付けているときに、文字(列)が(改行によって)入力
>されると、その内容は改行を含めていったんバッファにたくわえられます。
>このバッファは標準入力が開かれるときに用意されるものです。getchar()は
>標準入力から1文字取ってくる関数なので、結局、そのバッファ中のデータの
>最初の1文字を取ってくることになります。

例えば、下記の例で

while(*p) {
printf ("%c", *p);
p++;
}

char型のポインタ変数 p に何らかの文字列の先頭アドレスが格納されている
とします。 ここでは、インクリメントという作業をしているので、確実に
次の文字を指し示しているのはわかるのですが、前回の

do {
ch = getchar();
putchar('.');
} while(ch != '\n');

このコードでは、バッファに格納されているであろう、先頭文字以外の
文字はどのように指し示されているのでしょうか?(繰り返し処理の過程で)
バッファそのものが、そのような機能を前もって備えているので
しょうか?

>余談ですが、参考までに。
>改行なしで1文字読み込む非標準な関数にgetche()とgetch()というのが、
>conio.hかcurses.hにあります。この2つ関数の違いは、入力した文字を表示
>するかしないかです。詳細はライブラリリファレンスを参照してください。
>

私の環境では、conio.h で動作確認できました。それと新しい発見も
ありました。
現在 C言語を勉強するに当たって二つのコンパイラを使用しています。
一つは、LSI-C86 で、もう一つはインタプリター型の学習用ソフト
Ultra-C というソフトです。
Ultra-C では、非標準関数の getch() はサポートされてませんでした。
( getche() はサポートしています。)
LSI-C では、ばっちりサポートされてました。
同じコンパイラでも色々違いがあるもんだな? と一人で関心してました。
余談ですけどね...(~_~;)


No.998

Re:getchar() 関数に関係した質問です
投稿者---kikk(2002/02/02 02:16:02)


ども。


>>標準入力が入力を受け付けているときに、文字(列)が(改行によって)入力
>>されると、その内容は改行を含めていったんバッファにたくわえられます。
>>このバッファは標準入力が開かれるときに用意されるものです。getchar()は
>>標準入力から1文字取ってくる関数なので、結局、そのバッファ中のデータの
>>最初の1文字を取ってくることになります。

>do {
> ch = getchar();
> putchar('.');
>} while(ch != '\n');
>
>このコードでは、バッファに格納されているであろう、先頭文字以外の
>文字はどのように指し示されているのでしょうか?(繰り返し処理の過程で)
>バッファそのものが、そのような機能を前もって備えているので
>しょうか?

do〜while()の中にgetchar()があることはバッファとは関係ありません。
何度かでてきた「バッファ」は、標準入力に限らず入出力ストリーム一般に
おいて、オープンされるときにストリームに対して自動的に割り当てられ
ます。このバッファには普通はプログラム/プログラマは直接手を出さなくて
もいいようになっています(カプセル化されているということです)。

実際にどういう風に実現しているかは実装の話になりますので、これ、と
決め付けられません。というか、そういうことを考えなくてもいいように
カプセル化されているといったほうがいいかもしれません。しかしながら、
ストリームを扱う(ハンドリングする)型FILEの実装から、想像することは
できます。

FILEはstdio.hで定義されていますが、その実体は多分構造体で、ファイル
モード(テキスト/バイナリやr/w等)やバッファへのポインタや、バッファを
どこまで読んだかのポインタ等が多分含まれています。実際の処理系の実装
がどうなっているかは、その処理系のstdio.hを参照してください。LSI Cの
FILEの実装にはコメントがついているので、何をやっているのかなんとなく
推測できます。

今回取り上げられている、getchar()の実装のロジックのキモは、ptrが
バッファ中のデータの先頭を指しているとして、だいたい以下のように
なっていると推測できます。なんで「だいたい」かというと、EOFを返さ
なければならない状況や、バッファの最後にたどり着いてptrを先頭に
巻き戻さなければならない場合を考慮していないため。なのでかなり
いいかげんといえばいいかげん。FILEの実装を見ればいいかげんさが
きっとわかるでしょう。。

int getchar() { return *stdin->ptr++; }

LSI Cを含む多くの処理系ではgetchar()はfgetc(stdin)というようなマクロ
で実現されているので、上記はfgetc()の実装の推測といったほうがいいかも
しれません。ただしその場合は上記のstdinはfgets()の引数になりますが。

なお、基本的に普通は手が出せないことになっているストリームのバッファ
ですが、標準関数setbuf()やsetvbuf()で、間接的にそのサイズや動作を指示
することはできます。またfflush()のような関数もあります。これらの詳細
はリファレンスを。

また、ストリーム関係については、K&Rにて、fopen()の実装例が出ている
のでそちらも参考にしてみてください。この辺のことが理解できれば、
なんでFILE *でファイルを扱うのかもわかると思います。

ストリームやバッファといった話は実装に関わる問題なので、「たぶん」
とか「推測」とかいう言葉を使いました。これらに限らず、仕様と実装を
混同するといつかハマるのでご注意あれ。


では。

No.999

Re:getchar() 関数に関係した質問です
投稿者---めい助(2002/02/02 11:43:07)


kikk さん、ありがとうございました。
しかしながら悲しいかな私の現在のレベルでは kikk さんの
説明して下さった十分の一ぐらいしか理解できていません。(~_~;)

今現在、"独習C"という入門本で勉強しているのですが、取り組んでいる
最中で全体的なC言語の特性、と言うか繋がりみたいなものがわからなくて
悩んでいる部分があると思います。

ミクロな部分にばかり目が行って、全体的なマクロ的な考え方ができていない
ので、何か問題が起きてもそこだけで解決しよう! と思いうまくいかなかった
ケースがあったのかな... と振り返ればそんな気がします。

とりあえず、今取り組んでいる基本を最後まで押さえてから、もう一度
kikk さんの文面を読み返したいと思います。
その時にでも色々と質問させて頂くこともあろうかと思いますけど、
宜しくお願いします。



戻る


「初心者のためのポイント学習C言語」 Last modified:2002.03.16
Copyright(c) 2000-2002 TOMOJI All Rights Reserved