プロが教える店舗&オフィスのセキュリティ対策術

入力された数字を数値に変換するプログラムを作ったのですが、うまくいきません。

#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <string.h>
#include <ctype.h>

unsigned long long atollu(char[16]);


void main(void){

unsigned long long input;
char str[16];
char test[16] = "123456789012345";
int i,j=1;

//atollu の動作確認・・・(1)
printf("atollu test:%llu\n\n",atollu(test) );

//入力
NG:
printf("%d回目 数値を入力(1~100000000000000 終了:exit)>",j);

gets(str);
input = atollu(str);


//str と atollu(str) の確認
printf("%s %llu\n\n",str,atollu(str));

//exitと入力すると終了・・・(2)
if(strcmp(str, "exit") == 0){
exit(0);
}

//数字以外がないか確認・・・(3)
else {
for( i=0; i<=15; i++){
if(isdigit(str[i]) == 0){
printf("NG\n");
for( i=0; i<=16; i++){
str[i]='\0';
}
printf("moji\n");
j++;
goto NG;
}
}
}

//範囲外
if(input>100000000000000 || input==0){
printf("NG\n");
printf("hani\n");
j++;
goto NG;
}


printf("\n入力値は% llu",input );

}


unsigned long long atollu(char str[16]){

unsigned long long t=0;
int i,n;

n = strlen(str) - 1;

for( i=0; i<=15; i++){
if(str[i] == '\0'){ str[i] = 0;}
else { t = t + (str[i] - '0') * pow(10.0,n-i);}
}
str[16] = '\0';

return t;
}


/*
数字を数値に変換する関数atolluは自分で作りました。
これの動作は(1)でちゃんとした値が出ているのでうまくできているようです。
(2)も"exit"と入力すれば終了するのでうまくいっています。

また、例えば "12" と入力したとき

12 9

NG
moji

表示されますが、二回目以降は

12 12

NG
moji

となり、atollu(str)はうまく出力されている一方で、最後に「moji」と出てきているので、(3)でうまくいっていないようです。

どう直すべきなのか見当もつかないので困っています。
よろしくお願いします。

A 回答 (3件)

> str[ i ] - 48



せっかくなので str[i] - '0'
と書くと、'0'~'9'のコードが連続してさえいれば
アスキーコードかどうかを意識しなくてよくなります。
    • good
    • 0
この回答へのお礼

str[i] - '0'の方が一般化できていいのですね。
ありがとうございました。

お礼日時:2008/03/12 11:49

ご質問に対する直接的な回答ではないのですが、私の個人的な意見を述べさせていただきたいと思います。



質問内容のchar test[16]を見ると整数値を想定しているようなので、
整数値に限ってお話させていただきます。

おそらく想定している環境はアスキーコードで入力されることを
想定していると解釈いたしました。
そこで、アスキーコード表ではいわゆる数字の0~9は0~9という数字で定義されており、文字列の0~9は48~57という数字で定義されています。

そこで、以下のようなプログラムでは目的の動作を果たせないのでしょうか?

int main( ) {
int i ;
int output = 0 ;
char str[ 11 ] = { '\0' } ;

printf( "整数値で10桁まで入力できます : " ) ;
scanf( "%s", str ) ;

for( i = 0 ; str[ i ] != '\0' ; i ++ ) {
if( ( str[ i ] - 48 ) > 9 )
return 0 ;
if( ( str[ i ] - 48 ) < 0 )
return 0 ;
}

for( i = 0 ; str[ i ] != '\0' ; i ++ )
output = output * 10 + ( str[ i ] - 48 ) ;

printf( "%d\n", output ) ;
}

ひょっとするとyata16さんが意図している動作と違うかもしれませんが、参考までに意見させていただきました。

また、上のプログラムはscanfの特性を受けますので、間にスペース等が入ると意図しない動作になる可能性がありますので、注意してください。
    • good
    • 0
この回答へのお礼

コンセプトとして"scanf"を使わないようにしていたので、若干違いますが、本質的には同じです。動作も大丈夫でした。
ありがとうございました。

お礼日時:2008/03/12 11:48

★条件式がおかしい。


>//数字以外がないか確認・・・(3)
 この場所。
 間違い⇒for( i=0; i<=15; i++){
 正しい⇒for( i=0; str[i] != '\0'; i++){
 ※15文字と決め付けない方が良いでしょう。
・for文中のfor文で『i=0』にしているので正しく数字以外を判定できない。
 str配列は gets 関数で上書きされるため明示的に初期化しなくて良い。
 よってfor文中のfor文処理はいらない。削除すべき。
 for ( i = 0 ; str[i] != '\0' ; i++ ){
  if ( !isdigit(str[i]) ){
   printf("NG\n");
   printf("moji\n");
   j++;
   goto NG;
  }
 }
 これだけでよい。
・atollu関数の条件式もおかしい(安全でない)
 間違い⇒for( i=0; i<=15; i++){
 正しい⇒for( i=0; str[i] != '\0' ; i++){
 ※ここも15文字と決め付けない方が…。
 
 間違い⇒str[16] = '\0';
 正しい⇒str[15] = '\0';
 ※これは完全に良くありません。
 ※引数がstr[16]なのでstr[15]までしかアクセスしてはいけません。
 ※そもそもこの処理は必要なのですか?
・次の処理は意味がありません。
>if(str[i] == '\0'){
> str[i] = 0;
>}
 '\0'も 0 も同じ値ですよ。
 ※ソース上での表現の違いだけです。
 ※『0』も『'\0'』も『0x00』も同じ値(ゼロ)です。

サンプル:
unsigned long long atollu( char str[] )
{
 unsigned long long t = 0;
 int i;
 
//for ( i = 0 ; isdigit(str[i]) ; i++ ){
 for ( i = 0 ; str[i] != '\0' ; i++ ){
  t = (t * 10) + (str[i] - '0');
 }
 return t;
}
※pow関数を使わなくても出来ます。
※条件式の『str[i] != '\0'』よりも『isdigit(str[i])』の方が良い。
    • good
    • 0
この回答へのお礼

なるほど、すごく勉強になりました。
よく考えたら配列の最後まで見なくても空の部分まで見ればいいのは当たり前ですね・・・。
まだまだ勉強不足でした。
ありがとうございました。

お礼日時:2008/03/12 11:39

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