ページ 1 / 1
getchar関数について
Posted: 2013年4月27日(土) 19:53
by PPTM
初めまして。自分はC言語を学び始めて2週間程度の初心者です。
文字列を入力したのち、ある1文字を入力してそれが文字列内にあるかどうかを調べる関数を作ろうとしています。
おそらく関数のほうは何とかなったと思うのですが、『ある1文字を入力』する際に、getchar関数を呼びだしてもうまくいかず
いろいろいじっていたら同じものを2つ並べることで(見た目は)動くようになってしまいました。(38-39行目)
同じのを2つ並べる前は入力ストリームが出てきませんでした。
どうしてこういうことになってしまったかが知りたいです。よろしくお願いします。
コード:
#include <stdio.h>
/* 文字cがあればその配列の添え字を返す、なければ-1を返す関数 */
int str_char(const char str[], int c){
int i = 0 ;
while (1)
{
if( str[i] == c)
{
return (i);
}
if( str[i] == '\0')
{
return (-1);
}
i++;
}
}
int main(void)
{
char read[40] ;
int word ;
printf( "40文字以内の半角文字でなんか書いてね\n入力欄→:" );
scanf( "%s",read );
printf( "はいはい、%sですねーっと。\n",read );
printf( "\nその中に特定の文字があるか調べますよー。何にします?\n" );
word = getchar();
word = getchar(); /* なぜかこいつを二回入れると動くの。なんでなのさ(#-ω-) */
putchar(word);
printf( "ですね、了解。\n");
printf( "えーっと、そいつの添え字は[%d]っすね。\n", str_char( read, word));
return (0);
}
Re: getchar関数について
Posted: 2013年4月27日(土) 20:34
by Rittai_3D
scanfの第二引数はポインタですよ。
Re: getchar関数について
Posted: 2013年4月27日(土) 20:41
by box
3D_3D さんが書きました:scanfの第二引数はポインタですよ。
質問者さんのコードでは、そうなっています。
C言語において、配列名(今回の場合、read)は、
その配列の先頭要素(今回の場合、read[0])への
「ポインター」です。
3D_3Dさんは、scanf()をどのように書くべきだと思われていましたか?
もし、&read[0] と書くべきだと思われているとしたら、
それはそれで正しい書き方ではありますが、簡略化してreadとだけ書いても同じであります。
Re: getchar関数について
Posted: 2013年4月27日(土) 20:49
by box
PPTM さんが書きました:
どうしてこういうことになってしまったかが知りたいです。よろしくお願いします。
'a'を入力してからエンターキーを押したとします。
このとき、入力ストリームには
'a'(の文字コード)
エンターキー(の文字コード)
の2バイトを格納します。
PPTM さんが書きました:
コード:
word = getchar();
word = getchar(); /* なぜかこいつを二回入れると動くの。なんでなのさ(#-ω-) */
1個目のgetchar()の呼び出しで'a'を拾い上げ、
2個目のgetchar()の呼び出しでエンターキーを拾い上げます。
というわけで、最終的には、2個目のgetchar()で拾い上げた
エンターキーの文字コードがwordに入ります(2個目のgetchar()でもって、wordを上書きしているから)。
本来は'a'を拾い上げたいはずなので、このコードでは思ったとおりに動かないような気がします。
大丈夫でしたか?
Re: getchar関数について
Posted: 2013年4月27日(土) 21:05
by Rittai_3D
>>box様
すいません、自分はいつも簡略化せずに書いていたので…
ちょっと恥ずかしいですw
Re: getchar関数について
Posted: 2013年4月27日(土) 21:13
by PPTM
返信ありがとうございます。
自分で試してみた限りでは
配列に何か半角文字を入力し、Enterキー で確定した場合 →二つ目のgetcharのみが値を拾う
(配列を入力した後、例えば 01 と入力しEnterキーで確定した場合、wordには0が入る)
配列に何も半角文字を入力しないで Ctrl+z と Enterキー で確定した場合→二つとものgetcharが値を拾う
(同じように、例えば 01 と入力しEnterキーで確定した場合、wordには1が入る)
となってしまっているようです。なぜこのように挙動が違っているのかがさっぱりわからない状態です・・・
Re: getchar関数について
Posted: 2013年4月27日(土) 22:29
by nil
おそらくなのですが、
入力 01 であった際、
scanf関数が読み取るのは 01 のみであり、
入力ストリームには'\n'が取り残されているようで、
getchar関数がその残された'\n'を読んでしまうため、
該当の動作になるようです。
以下追記
► スポイラーを表示
このように少しコードを改変した結果、
コード:
int main(void)
{
char read[40] ;
int word ;
printf( "40文字以内の半角文字でなんか書いてね\n入力欄→:" );
scanf( "%s",read );
printf( "はいはい、%sですねーっと。\n",read );
printf( "\nその中に特定の文字があるか調べますよー。何にします?\n" );
word = getchar();
printf( "%dですね、了解。\n",word);
printf( "えーっと、そいつの添え字は[%d]っすね。\n", str_char( read, word));
return (0);
}
[入力]01
[出力]10ですね、了解
10='\n'=改行文字なので間違いなさそうです。
Re: getchar関数について
Posted: 2013年4月27日(土) 22:48
by PPTM
返信ありがとうございます。
成程!そういうことだったのですか。
何らかの手段で入力ストリームを初期化するなりすればいいのですね。すっきりしました。
Re: getchar関数について
Posted: 2013年4月27日(土) 23:07
by ただの屍のようです
scanfの仕様かもしれませんが、文字列入力してEnterを押すと、バッファー内にEnterが残るようです。
そして、Ctrl+z Enterするとバッファー内にはなにも残らないようです。
そこで、文字列を取り出したあとバッファーを一度クリアさせておく手を考えます。おそらくうまく動くコード載せます。
コード:
#include <stdio.h>
/* 文字cがあればその配列の添え字を返す、なければ-1を返す関数 */
int str_char(const char str[], int c){
int i = 0 ;
while (1)
{
if( str[i] == c)
{
return (i);
}
if( str[i] == '\0')
{
return (-1);
}
i++;
}
}
int main(void)
{
char read[40] ;
int word ;
printf( "40文字以内の半角文字でなんか書いてね\n入力欄→:" );
scanf( "%s",read );
printf( "はいはい、%sですねーっと。\n",read );
printf( "\nその中に特定の文字があるか調べますよー。何にします?\n" );
fflush(stdin);
word = getchar(); /* なぜかこいつを二回入れると動くの。なんでなのさ(#-ω-) */
putchar(word);
printf( "ですね、了解。\n");
printf( "えーっと、そいつの添え字は[%d]っすね。\n", str_char( read, word));
return (0);
}
Re: getchar関数について
Posted: 2013年4月27日(土) 23:11
by softya(ソフト屋)
fflush(stdin);は環境依存なので使わない方が良いです。
「[迷信] fflush で入力バッファをクリア | 株式会社きじねこ」
http://www.kijineko.co.jp/tech/supersti ... tream.html
Re: getchar関数について
Posted: 2013年4月27日(土) 23:18
by ただの屍のようです
サイト読んできました。
[引用]結局のところ、移植性を保ちながら確実に入力バッファをクリアするには、必要なだけ空読みするしかなさそうです。
この問題、Enterが残る場合と残らない場合があるのでfflush()のほかに手がないかと思います。
Re: getchar関数について
Posted: 2013年4月27日(土) 23:41
by softya(ソフト屋)
Ctrl+zとENTERだけだとscanfの戻り値が-1になるので検知できます。
「scanf - Wikipedia scanfの問題点と回避方法」
http://ja.wikipedia.org/wiki/Scanf#scan ... 9.E6.B3.95
「異常な入力が行われた時の処理」にも注目。
Re: getchar関数について
Posted: 2013年4月28日(日) 04:59
by ただの屍のようだ
対策法読みました。
演習問題ではいつも自作getch(配列を用意して、配列が空のときのみgetcharを行う)と自作putch(1文字を配列に書き込む)で済ましてきたので。
scanfの使い方はほとんど知りませんでした。
Re: getchar関数について
Posted: 2013年4月28日(日) 15:11
by softya(ソフト屋)
ただの屍のようだ さんが書きました:対策法読みました。
演習問題ではいつも自作getch(配列を用意して、配列が空のときのみgetcharを行う)と自作putch(1文字を配列に書き込む)で済ましてきたので。
scanfの使い方はほとんど知りませんでした。
入門書などでscanf()はよく出てきますし、学校の課題としても良く使われてますが、仕様が複雑なのでちゃんと使いこなすのは結構難しい関数なのです。
「scanf() 難しい」で検索すれば沢山そういう話が出てきます。
戻り値を必ずチェックして、EOFが混ざったりした時の対処やエラー処理をちゃんと書く練習にはなると思います。全部対応するのは初心者向けじゃないですが。
[補足]
今回の場合だと、CTRL+Zまで対処の必要性は感じませんので、%cを上手く使ってもらう程度で良いと思います。
Re: getchar関数について
Posted: 2013年4月28日(日) 16:40
by ISLe
ちょうどいま別のスレにも書かれていたことですが、
scanf(" ");
で改行含む空白系文字を読み飛ばすことができます。
fflush(stdin)は未定義の動作だしコンソールではうまく動いてもリダイレクトすると悲惨だったりします。
質問に書かれたコードも、入力する文字列の後ろに空白があると、読み取られない空白がgetcharに流れます。
こうするとかんたんです。
コード:
int main(void)
{
char read[40] ;
int word ;
printf( "40文字以内の半角文字でなんか書いてね\n入力欄→:" );
scanf( "%s",read );
printf( "はいはい、%sですねーっと。\n",read );
printf( "\nその中に特定の文字があるか調べますよー。何にします?\n" );
scanf(" ");
word = getchar();
putchar(word);
printf( "ですね、了解。\n");
printf( "えーっと、そいつの添え字は[%d]っすね。\n", str_char( read, word));
return (0);
}
#これでも文字列の途中に空白を入れるとダメですが。
(追記)
探したい文字に' 'を使うこともできませんね。
scanfの%sで文字列を読み込むのだから仕様ってことで不都合ないですけど。