こんにちはゲストさん。会員登録(無料)して質問・回答してみよう!

解決済みの質問

数字文字をカウントするプログラムの動作について

良い質問のタイトルが思い浮かばず、分かりづらいタイトルで申し訳ありません、C言語について質問させて頂きます。
C言語の参考書を買って夏休み中にプログラムの勉強をしているのですが、何故動作するのかがわからない例があります、ソースは以下の通りです。

#include <stdio.h>

int main(void)
{
int i,ch;
int cnt[10] = {0};

while(1)
{
ch = getchar();
if (ch==EOF) break;

switch(ch)
{
case '0' : cnt[0]++;printf("%d\n",ch);break;/* printfは確認の為 */
case '1' : cnt[1]++;printf("%d\n",ch);break;
case '2' : cnt[2]++;printf("%d\n",ch);break;
case '3' : cnt[3]++;printf("%d\n",ch);break;
case '4' : cnt[4]++;printf("%d\n",ch);break;
case '5' : cnt[5]++;printf("%d\n",ch);break;
case '6' : cnt[6]++;printf("%d\n",ch);break;
case '7' : cnt[7]++;printf("%d\n",ch);break;
case '8' : cnt[8]++;printf("%d\n",ch);break;
case '9' : cnt[9]++;printf("%d\n",ch);break;
}
}

puts("数字文字の出現回数");
for(i=0;i<10;i++)
printf("'%d':%d\n",i,cnt[i]);
getchar();getchar();

return(0);
}

といったプログラムです。

実行し、数値を入力、CTRL+Zで入力を終了し、出現回数を表示させる、という動作自体は無事にできるのですが、何故chの値が変更していくのかがわかりません、数値を入力した時点で51や49といったそれぞれ違う数値が表示されるのですが、chの値を変更させる命令を、何が引き起こしているのかが理解できません、3(51)と判定されて同じ数が無限にカウントされないのは何故なのでしょうか・・・・?

また、その後の無限ループからの脱出をCTRL+Zがどうして引き起こすのかも理解できず困っています、教科書には「CTRL+Zは入力の終了を意味する」とあるのですが、これは一体どういう意味なのでしょうか、強制的に割り込んでEOFを代入するということなのでしょうか・・・?

お時間がある時にでも、教えて頂けると助かります、よろしくお願いします。

投稿日時 - 2011-08-18 11:11:19

QNo.6950388

困ってます

質問者が選んだベストアンサー

>getchar関数とは入力した文字を順番に3、.、1、4、と返す関数ということなのでしょうか・・?

基本的にはそれで合ってます。
ただ、'3'、'.'、'1'、'4'という文字そのものを返す訳ではなく、アスキーコードを返します。
http://e-words.jp/p/r-ascii.html

平たくいうと、コンピュータは0と1の世界ですから、'a'などの文字も数値としてハードディスクに保存する必要があります。人間が見て分かる文字とコンピュータの世界のコードとの対応表がアスキーコード表です。
OSを始め様々なソフトが、この保存された数値を人間が見て分かりやすい文字に変換してくれてるわけです。


今回の例で言うと、入力された数値をアスキーコードではなく、配列の添字として使いたいので、
int index = ch - '0';
としています。

int index = ch - 48;
でもいいんですが、マジックナンバー(プログラム中に書かれた0, 1以外の数値)を嫌う人もいますので、'0'としています。



printfで%dで出力すると、アスキーコードの値がそのまま出力されます。
これを入力した文字そのものを出力したい場合は、%cを使用します。

投稿日時 - 2011-08-18 20:12:31

このQ&Aは役に立ちましたか?

0人が「このQ&Aが役に立った」と投票しています

回答(6)

ANo.6

ちょっと混乱の元になるかもしれませんが、豆知識として。

getchar()が返すのは、「キーボードから」入力した1文字ものではなく、「標準入力から」入力した1文字です。
標準入力は通常はキーボードに割り当てられているので、何もしなければ「キーボードから」になります。
実行ファイル < input.txt とすると、標準入力はファイル input.txt からになります。
別のコマンド | 実行ファイル とすると、標準入力は「別のコマンド」の標準出力に出力されたものになります。

getcharは入力の終わりが来たらEOFという特別な値を返して報せます。このプログラムでは、そのEOFが返ってきたら、breakでループから抜け出します。
入力がファイルならその終わりは明確です。
しかし、キーボードでは「終わり」などありません。そこで、特別なコードを入力したら「キーボードからの入力の終わり」だと処理することにしています。これがWindowsではCTRL+Z,Unix系ではCTRL+Dです。


文字を表わすのに使われるのはASCIIコード(とその互換コード)だけではありません。例えば、一部大型コンピュータではEBCDICというASCIIとまったく違うものが使われています。当然、そう言うマシンでは、getchar()はASCIIではなく、それぞれのコードを返します。
'0'と書いた方がよく、48と直接書くべきではない理由は、ASCIIコードとは限らないから、ということもあります。
※ 一応、'0'から'9'までが連続していることは規格に保証されているですが。

まあ、こんな練習用コードを他の環境で動かすことも無いし、ASCII以外のコードを使ってる環境なんて、一般人はめったに触れることもないくらいにASCIIが普及しているので、間違いとまではいいきれませんが。
それでも「ASCIIだと理解して使う」のと「ASCIIだと思い込んで使ってしまう」のとでは違うので。

投稿日時 - 2011-08-18 23:39:25

ANo.5

3.14 という入力なら, getchar は「'3', '.', '1', '4' という文字を (順に) 返す」というのがむしろ適切ではないかと>#4. 「ASCII コードを返す」までは規定されてないし.

投稿日時 - 2011-08-18 22:57:30

ANo.3

1.まず0~9の文字が入力された回数を覚えておく為の入れ物(配列)cntを用意
2.getchar()で、キーボードからの入力結果をchに代入
3.入力された文字を判定し、カウントアップ
4.結果を出力

という流れになります。

++という演算子は、今の値に1プラスするという意味なので、これでカウントアップできている訳です。

無限にカウントされないのは、無限ループの最初でgetchar()を呼んでるからです。
switch文で判定を行ってカウントアップした後、すぐにgetchar()が呼ばれて、キーボードからの入力待ち状態になるからです。



ちなみに、余計に混乱するかもしれませんけど、余談としてこのプログラムの改善点を書いておきます。

出現回数を配列に入れてますが、最後の結果出力でしかその恩恵を受けていません。
せっかく配列を使用してるので、冗長なswitch文の判定箇所をもっとコンパクトにできます。

swich文の箇所を以下にコードに書き換えても、全く同じに動きます。

int index = ch - '0';
if (index >= 0 && index <= 9)
cnt[index]++;

投稿日時 - 2011-08-18 18:04:01

お礼

解りやすいご説明ありがとうございます。
おかげで無限ループに入らない理由もわかることができましたし、改善点も教えて頂いて短くすることもできました。
ただ一つだけまだわからないことがあるのですが、例えば3.14と入力した時、chの値が51→49→52と変化して行くのは何故なのでしょうか、chの値が変化する要因がどの部分なのかが理解できていなくて・・・、getchar関数とは入力した文字を順番に3、.、1、4、と返す関数ということなのでしょうか・・?

投稿日時 - 2011-08-18 18:35:29

ANo.2

Wr5

>数値を入力した時点で51や49といったそれぞれ違う数値が表示されるのですが

とりあえず、「数値」と「数字」の違いは区別できる様になっておいた方が良いでしょう。

>教科書には「CTRL+Zは入力の終了を意味する」とあるのですが、これは一体どういう意味なのでしょうか、強制的に割り込んでEOFを代入するということなのでしょうか・・・?

Windowsのコマンドプロンプト(古くはDOS)で、「標準入力からEOFを入力する手段」がCTRL+Zとなっているだけです。
環境が変わるとCTRL+Zではなくなる場合もありますので誤解無きよう。
# LinuxのbashだとCTRL+Dですし。

投稿日時 - 2011-08-18 12:50:44

ANo.1

>何故chの値が変更していくのかがわかりません

ch = getchar();
で「chに、getchar()関数からの戻り値を代入している」のは理解していますか?

getchar()関数が「何を返すか?」を理解していますか?

getchar()関数は、キーボード入力に応じて色々な値を返すって事を理解できていますか?

getchar()は「キーボードから入力された文字を返す関数」なのは理解できていますか?

>その後の無限ループからの脱出をCTRL+Zがどうして引き起こすのかも理解できず困っています

if (ch==EOF) break;
のif文が「chがEOFならbreak文によってループを抜ける」ってのは理解できてますか?

getchar()がEOFを返すと、chの値がEOFになるのは理解できてますか?

つまり「getchar()がEOFを返すとループを抜ける」って事なんですが、それは理解できてますか?

>教科書には「CTRL+Zは入力の終了を意味する」とあるのですが、これは一体どういう意味なのでしょうか

「CTRL+Zを入力すると、入力終了状態になり、getchar()がEOFを返す」って意味。

getchar()がEOFを返してきたら、どういう動作になるか、理解できていますか?

投稿日時 - 2011-08-18 11:33:00

あなたにオススメの質問