プロが教えるわが家の防犯対策術!

以下のプログラムの理解について、質問です。


入力した文字列に、0 ~ 9 の数字がそれぞれ何度含まれていたかをカウントするプログラムです。
(明解C言語 入門編 P219)

==========
プログラム
==========

#include <stdio.h>

void str_dcount(const char str[], int cnt[])
{
unsigned i = 0;
while(str[i]) { // str に '\0' が現れるまで繰り返し
if(str[i] >= '0' && str[i] <= '9')
cnt[str[i] - '0']++; // !!!! ココです !!!!
i++;
}
}

int main(void)
{
int i;
int dcnt[10] = {0};
char str[100];

printf("文字列を入力してください : " );
scanf("%s", str); // 文字列を str へ

str_dcount(str, dcnt); // 引数  str = 入力した文字列, dcnt = すべて '0'

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


return 0;
}


=========
実行結果
=========

文字列を入力してください : 3.14159
数字文字の出現回数
'0' : 0
'1' : 2
'2' : 0
'3' : 1
'4' : 1
'5' : 1
'6' : 0
'7' : 0
'8' : 0
'9' : 1

==========
質問内容
==========

関数 str_dcount 内の 『 cnt[str[i] - '0']++; 』 がどのような処理を行っているのか、理解ができません。
以下のように考えたのですが、それでは上手く動かないとの結論に至りました。

たとえば、i = 0 で、str[100] = 3.14159 であるとき、

/* ループ1回目 */
cnt[0] = str[0] - 0; /* 3 - 0 で cnt[0] には 3 が入る。 */
cnt[0++];

/* ループ2回目 */
cnt[1] = str[1] - 1; /* "." - 1 となる。 数字ではないので減算できずエラーになるのでは? */
cnt[1++];

/* ループ3回目 */
cnt[2] = str[2] - 2; /* 1 - 2 なので、なぜか負の数になってしまう。。 */
cnt[2++];


配列の添え字が、配列になっている cnt[str[i]] までは理解できますが、
以下の2点が解っていないために理解ができていないようです。

1) cnt[str[i] - '0']++ ;
の - '0' が何を行っているのか

配列には数字が入っており、そこから文字 '0' を減算しているのでしょうか?
数字から文字を減算できるのでしょうか?


2)  ++ 
はどの値を増加させているのでしょうか?



宜しくお願いします。

A 回答 (4件)

unsigned i = 0;


while(str[i]) { // str に '\0' が現れるまで繰り返し
if(str[i] >= '0' && str[i] <= '9')
cnt[str[i] - '0']++; // !!!! ココです !!!!
i++;

str[]に"3.14159"が入っていたとき、
str[0] == '3'であるから、if文の条件を満たします。
str[0] - '0'は、'3' - '0'ですから、3という数値です。
たいていの文字コードでは'0', '1, '2', ..., '9'の順に番号が振ってあるはずですから、
'0'を基準としていくつ離れているか('3' - '0'の式の部分)が、その数字に相当します。
cnt[3]++;は、配列の要素cnt[3]の値を1だけ増やします。
これで、"3.14159"の先頭文字である'3'の個数が1個増えたことになります。
i++;によって、次の文字'.'を対象とします。
これは、数字ではありませんので、if文をスルーします。
i++;によって、次の文字'1'を対象とします。
ここの処理は、先ほどの'3'の場合と同じです。cnt[1]を1増やします。
以下同様です。
    • good
    • 0
この回答へのお礼

丁寧なご回答有難うございました。

おかげさまで理解することが出来ました。

お礼日時:2013/01/13 17:14

if(str[i] >= '0' && str[i] <= '9')


cnt[str[i] - '0']++; // !!!! ココです !!!!
これを
if(str[i] >= '0' && str[i] <= '9'){
printf("%d回目,str[i]=%d,str[i]-'0'=%d,before:cnt[%d]=%d",i,str[i],str[i]-'0',str[i]-'0',cnt[str[i] - '0']);
cnt[str[i] - '0']++; // !!!! ココです !!!!
printf(",after:cnt[%d]=%d\n",str[i]-'0',cnt[str[i] - '0']);
}
これに書き直して実行ささせてみなさい。
    • good
    • 0
この回答へのお礼

ご回答有難うございました。おかげさまで理解が進みました。

ベストアンサーにすることができず恐縮ですが、皆様の
回答を読むことで理解度が高まり、感謝しております。

お礼日時:2013/01/13 17:19

文字データは特定の数値で符号化されています.


ひとまず,ASCIIコード表と照らし合わせるのが分かりやすいでしょう.

1) - '0' が何を行っているのか

 '0'はASCIIコードで48(十六進数で0x30)です.
 '3'は同じく51(十六進数で0x33)です.
 よって '3'-'0' は 51-48 を意味し,結果は数値の 3 です.


2) ++ はどの値を増加
 上の例('3'-'0')なら,'3'の度数(cnt[3])を増やします.



/* ループ1回目 */
cnt[3]++; /* '3'-'0' = 51-48 = 3 より */

/* ループ2回目 */
/*
 if(str[i] >= '0' && str[i] <= '9') は
 if(str[i] >= 48 && str[i] <= 57) であり,
 '.' == 46 なので 偽となる
*/

/* ループ3回目 */
cnt[1]++; /* '1'-'0' = 49-48 = 1 より */

以下同様.
    • good
    • 0
この回答へのお礼

ご回答有難うございました。おかげさまで理解が進みました。

ベストアンサーにすることができず恐縮ですが、皆様の
回答を読むことで理解度が高まり、感謝しております。

お礼日時:2013/01/13 17:18

数字の文字コードは連続していると仮定(念のため)



> /* ループ1回目 */
> cnt[0] = str[0] - 0; /* 3 - 0 で cnt[0] には 3 が入る。 */
> cnt[0++];

str[0]は'3'で、str[i]-'0'='3'-'0'=3です。この値がcntの添え字になります。
なので、cnt[3]++;が実行されます。++の位置も違いますね。

> /* ループ2回目 */
> cnt[1] = str[1] - 1; /* "." - 1 となる。 数字ではないので減算できずエラーになるのでは? */
> cnt[1++];

その前のif文で'.'は排除されています。

> /* ループ3回目 */
> cnt[2] = str[2] - 2; /* 1 - 2 なので、なぜか負の数になってしまう。。 */
> cnt[2++];

str[2]-'0'ですよね。なんで-2になってるんですか?
'2'-'0'=2なので、cnt[2]++;が実行されます。
    • good
    • 0
この回答へのお礼

ご回答有難うございました。おかげさまで理解が進みました。

ベストアンサーにすることができず恐縮ですが、皆様の
回答を読むことで理解度が高まり、感謝しております。

お礼日時:2013/01/13 17:17

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