C言語関係掲示板

過去ログ

No.228.第8章の標準入出力関数(2)の演習問題


No.1390

質問です
投稿者---MK(2002/04/05 20:59:55)


はじめまして
今、勉強のためにこのページを読ませてもらっています。
そこで早速質問なんですが、第8章の標準入出力関数(2)の最後の演習問題で
(点数の分布の問題です)解答例で実行してみたのですが、下記の点で質問です。
・何も入力せずにEnterだけを押した場合
・スペースキーをおしてEnterを押した場合
この2つのケースについてなんですが、両方とも'0'と判断されてしまうらしく
点数の人数を分布させるときにその分が含まれてしまいます。
まだ勉強したてで詳しくわからないので、ここで質問させていただきました。
説明がわかりにくくてすみませんが、ぜひ教えてください。
よろしくお願いします。

No.1391

Re:質問です
投稿者---MK(2002/04/05 21:06:48)


度々申し訳ありません。
先ほどの説明に以下の内容を付加します。

問題では、gets()関数を使っています。
このgetsがスペースを読み込めるという事なので、理由はそれだと思ったのですが、どうなんでしょうか?
また、そうであれば修正の仕方を教えて下さい。
よろしくお願いします。


No.1393

Re:質問です
投稿者---ともじ(2002/04/06 00:08:11)


こんばんは。

> そこで早速質問なんですが、第8章の標準入出力関数(2)の最後の演習問題で
> (点数の分布の問題です)解答例で実行してみたのですが、下記の点で質問です。
> ・何も入力せずにEnterだけを押した場合
> ・スペースキーをおしてEnterを押した場合
> この2つのケースについてなんですが、両方とも'0'と判断されてしまうらしく
> 点数の人数を分布させるときにその分が含まれてしまいます。

この問題が正常に動くのは、最初に'e'or'E'を入力した場合と、
0〜100の数値を入力した場合だけです。ですから、スペースやEnterはもちろん
記号や英字を入力した場合も、atoi()関数に値を渡すようになり、ご指摘のように
点数に含まれてしまいます。
実務で使われるようなプログラムではまずいのですが、単なる演習ということで、
エラーチェックはしていません。

> 問題では、gets()関数を使っています。
> このgetsがスペースを読み込めるという事なので、
> 理由はそれだと思ったのですが、どうなんでしょうか?

確かにscanf()関数を使うとスペースは読み込みませんので、
スペースが点数に反映することはありません。ですが、記号、英字が
点数に反映してしまう点では一緒です。

> また、そうであれば修正の仕方を教えて下さい。

回避策としては、atoi()で変換する前に、入力値のチェックを行い、
整数以外の場合には再入力を促すようにするべきでしょう。

以下に修正プログラムを提示します。

#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
int digitchk(char *s); 

int main(void)
{
        char    str[80];
        int     ten,i;
        int     ninzuu[11] = {0};          /* 人数の0クリア */

        puts("点数を入力しなさい。(終了条件:'e' あるいは 'E')");
        while(1) {
                gets(str);
		/* 'e' or 'E' の入力で終了 */
                if (str[0]=='e' || str[0]=='E') {
                        break;
                }
	       /* 整数値なら */
	       if (digitchk(str)==0) {
	       /* 点数に変換 */
                       ten = atoi(str);
                       if(ten>=0 && ten<=100) {
                  	            ninzuu[ten/10]++;
                       }
	       }
	       else 
	              printf("点数を入力しなさい。\n");
        }
        for (i=0;i<10;i++)
                printf("%3d〜%3d点は%3d人\n",i*10,i*10+9,ninzuu[i]);
        printf("100点は     %3d人\n",ninzuu[10]);

        return(0);
}

/* 整数値か否かのチェック 
  return 0: 整数値  return 1:非整数値 */
int digitchk(char *s)
{
	/* NUL文字入力時の処理 */
	if (*s=='\0') return(1);

	/* 数値のチェック */
	while (*s!='\0') {
		if (isdigit((int)*s)==0)
			return(1);
		s++;
	}
	return(0);
}


No.1397

追記
投稿者---ともじ(2002/04/06 10:13:01)


整数値か否かのチェックをしている digitchk関数で 

 /* NUL文字入力時の処理 */
 if (*s=='\0') return(1);

という部分がありますが、これはEnterのみを入力した場合に
NUL文字('\0')が入ってしまうので、それをチェックするためのものです。


No.1401

Re:質問です
投稿者---かずま(2002/04/06 22:15:35)


isdigit((int)*s) ですが、この (int) にはどういう意味があるので
しょうか。

*s の型は char ですから、それが符号付きの処理系、たとえば
gcc, Visual C++, LSI C-86 であれば、-128〜127 の値をとります。
int にキャストしても値は変わりません。ところが isdigit などの
<ctype.h> で宣言されている関数の引数は、unsiged char で表現可
能な値か、EOF の値でなければなりません。先に挙げた処理系だと
-1〜255 だということです。

したがって、isdigit((unsigned char)*s) と書くべきでしょう。
そうでないて、間違って漢字の入力があった場合におかしなことに
なる可能性があります。


No.1404

Re:質問です
投稿者---ともじ(2002/04/07 17:29:37)


ご指摘ありがとうございます。

>したがって、isdigit((unsigned char)*s) と書くべきでしょう。
>そうでないて、間違って漢字の入力があった場合におかしなことに
>なる可能性があります。

全角コードが入力されることについては考えていませんでした。
確かにそうですね。
isdigit()の引数が「int」なので型を合わせるために単にキャストしま
したが、全角入力を考えると文字検査関数には(unsigned char)での
キャストが必要になるということですね。

念のため、確認したソースを示します。
#include	<stdio.h>

int main(void)
{
	char	*p1 = "ABC";
	char	*p2 = "ABC";
	
	while (*p1) {
		printf("char:%#x unsigned char:%#x int:%#x\n",
				*p1,(unsigned char)*p1,(int)*p1);
		p1++;
	}

	while (*p2) {
		printf("char:%#x unsigned char:%#x int:%#x\n",
				*p2,(unsigned char)*p2,(int)*p2);
		p2++;
	}
	
	return(0);
}

出力結果
char:0x41 unsigned char:0x41 int:0x41
char:0x42 unsigned char:0x42 int:0x42
char:0x43 unsigned char:0x43 int:0x43
char:0xffffff82 unsigned char:0x82 int:0xffffff82
char:0x60 unsigned char:0x60 int:0x60
char:0xffffff82 unsigned char:0x82 int:0xffffff82
char:0x61 unsigned char:0x61 int:0x61
char:0xffffff82 unsigned char:0x82 int:0xffffff82
char:0x62 unsigned char:0x62 int:0x62

BCC55で確認しましたが、全角時にはキャスト無しと(int)でのキャストでは
0〜255に収まらないので、動作が未定義になるのですね。


戻る


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