getchar関数について

フォーラム(掲示板)ルール
フォーラム(掲示板)ルールはこちら  ※コードを貼り付ける場合は [code][/code] で囲って下さい。詳しくはこちら
PPTM

getchar関数について

#1

投稿記事 by PPTM » 7年前

初めまして。自分は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);
}

Rittai_3D
記事: 524
登録日時: 7年前

Re: getchar関数について

#2

投稿記事 by Rittai_3D » 7年前

scanfの第二引数はポインタですよ。
初心者です

box
記事: 1746
登録日時: 9年前

Re: getchar関数について

#3

投稿記事 by box » 7年前

3D_3D さんが書きました:scanfの第二引数はポインタですよ。
質問者さんのコードでは、そうなっています。
C言語において、配列名(今回の場合、read)は、
その配列の先頭要素(今回の場合、read[0])への
「ポインター」です。

3D_3Dさんは、scanf()をどのように書くべきだと思われていましたか?
もし、&read[0] と書くべきだと思われているとしたら、
それはそれで正しい書き方ではありますが、簡略化してreadとだけ書いても同じであります。
バグのないプログラムはない。
プログラムは思ったとおりには動かない。書いたとおりに動く。

box
記事: 1746
登録日時: 9年前

Re: getchar関数について

#4

投稿記事 by box » 7年前

PPTM さんが書きました: どうしてこういうことになってしまったかが知りたいです。よろしくお願いします。
'a'を入力してからエンターキーを押したとします。
このとき、入力ストリームには
'a'(の文字コード)
エンターキー(の文字コード)
の2バイトを格納します。
PPTM さんが書きました:

コード:

	word = getchar();
	word = getchar();		/* なぜかこいつを二回入れると動くの。なんでなのさ(#-ω-) */
1個目のgetchar()の呼び出しで'a'を拾い上げ、
2個目のgetchar()の呼び出しでエンターキーを拾い上げます。
というわけで、最終的には、2個目のgetchar()で拾い上げた
エンターキーの文字コードがwordに入ります(2個目のgetchar()でもって、wordを上書きしているから)。
本来は'a'を拾い上げたいはずなので、このコードでは思ったとおりに動かないような気がします。
大丈夫でしたか?
バグのないプログラムはない。
プログラムは思ったとおりには動かない。書いたとおりに動く。

Rittai_3D
記事: 524
登録日時: 7年前

Re: getchar関数について

#5

投稿記事 by Rittai_3D » 7年前

>>box様

すいません、自分はいつも簡略化せずに書いていたので…

ちょっと恥ずかしいですw
初心者です

PPTM

Re: getchar関数について

#6

投稿記事 by PPTM » 7年前

返信ありがとうございます。


自分で試してみた限りでは
配列に何か半角文字を入力し、Enterキー で確定した場合 →二つ目のgetcharのみが値を拾う
(配列を入力した後、例えば 01 と入力しEnterキーで確定した場合、wordには0が入る)

配列に何も半角文字を入力しないで Ctrl+z と Enterキー で確定した場合→二つとものgetcharが値を拾う
(同じように、例えば 01 と入力しEnterキーで確定した場合、wordには1が入る)

となってしまっているようです。なぜこのように挙動が違っているのかがさっぱりわからない状態です・・・

nil
記事: 428
登録日時: 8年前

Re: getchar関数について

#7

投稿記事 by nil » 7年前

おそらくなのですが、
入力 01 であった際、
scanf関数が読み取るのは 01 のみであり、
入力ストリームには'\n'が取り残されているようで、
getchar関数がその残された'\n'を読んでしまうため、
該当の動作になるようです。

以下追記
► スポイラーを表示
最後に編集したユーザー nil on 2013年4月28日(日) 08:39 [ 編集 1 回目 ]

PPTM

Re: getchar関数について

#8

投稿記事 by PPTM » 7年前

返信ありがとうございます。

成程!そういうことだったのですか。
何らかの手段で入力ストリームを初期化するなりすればいいのですね。すっきりしました。

ただの屍のようです

Re: getchar関数について

#9

投稿記事 by ただの屍のようです » 7年前

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);
}

アバター
softya(ソフト屋)
副管理人
記事: 11677
登録日時: 9年前
住所: 東海地方
連絡を取る:

Re: getchar関数について

#10

投稿記事 by softya(ソフト屋) » 7年前

fflush(stdin);は環境依存なので使わない方が良いです。
「[迷信] fflush で入力バッファをクリア | 株式会社きじねこ」
http://www.kijineko.co.jp/tech/supersti ... tream.html
by softya(ソフト屋) 方針:私は仕組み・考え方を理解して欲しいので直接的なコードを回答することはまれですので、すぐコードがほしい方はその旨をご明記下さい。私以外の方と交代したいと思います(代わりの方がいる保証は出来かねます)。

ただの屍のようです

Re: getchar関数について

#11

投稿記事 by ただの屍のようです » 7年前

サイト読んできました。
[引用]結局のところ、移植性を保ちながら確実に入力バッファをクリアするには、必要なだけ空読みするしかなさそうです。

この問題、Enterが残る場合と残らない場合があるのでfflush()のほかに手がないかと思います。

アバター
softya(ソフト屋)
副管理人
記事: 11677
登録日時: 9年前
住所: 東海地方
連絡を取る:

Re: getchar関数について

#12

投稿記事 by softya(ソフト屋) » 7年前

Ctrl+zとENTERだけだとscanfの戻り値が-1になるので検知できます。

「scanf - Wikipedia scanfの問題点と回避方法」
http://ja.wikipedia.org/wiki/Scanf#scan ... 9.E6.B3.95
「異常な入力が行われた時の処理」にも注目。
by softya(ソフト屋) 方針:私は仕組み・考え方を理解して欲しいので直接的なコードを回答することはまれですので、すぐコードがほしい方はその旨をご明記下さい。私以外の方と交代したいと思います(代わりの方がいる保証は出来かねます)。

ただの屍のようだ

Re: getchar関数について

#13

投稿記事 by ただの屍のようだ » 7年前

対策法読みました。
演習問題ではいつも自作getch(配列を用意して、配列が空のときのみgetcharを行う)と自作putch(1文字を配列に書き込む)で済ましてきたので。
scanfの使い方はほとんど知りませんでした。

アバター
softya(ソフト屋)
副管理人
記事: 11677
登録日時: 9年前
住所: 東海地方
連絡を取る:

Re: getchar関数について

#14

投稿記事 by softya(ソフト屋) » 7年前

ただの屍のようだ さんが書きました:対策法読みました。
演習問題ではいつも自作getch(配列を用意して、配列が空のときのみgetcharを行う)と自作putch(1文字を配列に書き込む)で済ましてきたので。
scanfの使い方はほとんど知りませんでした。
入門書などでscanf()はよく出てきますし、学校の課題としても良く使われてますが、仕様が複雑なのでちゃんと使いこなすのは結構難しい関数なのです。
「scanf() 難しい」で検索すれば沢山そういう話が出てきます。

戻り値を必ずチェックして、EOFが混ざったりした時の対処やエラー処理をちゃんと書く練習にはなると思います。全部対応するのは初心者向けじゃないですが。

[補足]
今回の場合だと、CTRL+Zまで対処の必要性は感じませんので、%cを上手く使ってもらう程度で良いと思います。
by softya(ソフト屋) 方針:私は仕組み・考え方を理解して欲しいので直接的なコードを回答することはまれですので、すぐコードがほしい方はその旨をご明記下さい。私以外の方と交代したいと思います(代わりの方がいる保証は出来かねます)。

ISLe
記事: 2646
登録日時: 9年前
連絡を取る:

Re: getchar関数について

#15

投稿記事 by ISLe » 7年前

ちょうどいま別のスレにも書かれていたことですが、
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で文字列を読み込むのだから仕様ってことで不都合ないですけど。

閉鎖

“C言語何でも質問掲示板” へ戻る