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

今C言語で加減剰余のプログラムを作っていて、
オーバーフローのチェックだけが上手くいきません。
limits.hをインクルードして、オーバーフローのチェック
を行いたいのですがどうすればよいのでしょうか?
扱いたい範囲はint型の-2147483648~2147483647です。
ちなみに開発環境はvc++2005です。

一応ソースを載せておきますので、よろしくお願いします。
#pragma warning ( disable : 4996 )

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

int main ( int argc, char *argv[] )
{
int start;//最初に入力する値(引数1)
int end;//最後に入力する値(引数3)
int kekka;//計算結果を格納
int amari;//除算の余りを格納
int max = INT_MAX;//表すことが出来る最大値
int min = INT_MIN;//表すことが出来る最小値

char enzansi;//入力する演算子 (引数2)
char str1[512];//格納用の配列を宣言(start用)
char str2[512];//格納用の配列を宣言(end用)

/*引数の個数チェック */
if ( argc != 4 ) {
printf ( "usage : %s start enzansi end\n", argv[0] );
exit ( 0 );
}

/*引数を取得して整数型に変換*/
start = atoi ( argv[1] );
end = atoi ( argv[3] );

/*引数の取得*/
enzansi = ( argv[2][0] );

sprintf ( str1, "%d", start );//str1に値を格納
sprintf ( str2, "%d", end );//str2に値を格納

/*str1とargv[1]による文字列の比較 */
if ( strcmp ( str1, argv[1] ) ) {
printf ( "error : 整数を入力して下さい。\n" );
exit ( 0 );

/*str2とargv[3]による文字列の比較 */
} else if ( strcmp ( str2, argv[3] ) ) {
printf ( "error : 整数を入力して下さい。\n" );
exit ( 0 );

/*argv[2]が2文字以上の場合 */
} else if ( strlen ( argv[2] ) >= 2 ) {
printf ( "error : 演算子を正しく入力して下さい。\n" );
exit ( 0 );
}

/*入力された演算子の条件ごとに計算 */
if ( enzansi == '+' ) {
kekka = start + end;
} else if ( enzansi == '-' ) {
kekka = start - end;
} else if ( enzansi == '*' ) {
kekka = start * end;
} else if ( enzansi == '/' ) {

/*startの値を0で除算する場合 */
if ( end == 0 ) {
printf ( "error : 0による除算は禁止です。\n" );
exit ( 0 );
}

kekka = start % end;//剰余を求める

/*割り算の結果として余りが出なかった場合 */
if ( kekka == 0 ) {
kekka = start / end;
printf ( "%d %c %d = %d", start, enzansi, end, kekka );
exit ( 0 );

/*割り算の結果として余りが出た場合 */
} else if ( kekka != 0 ) {
kekka = start / end;
amari = start % end;
printf ( "%d %c %d = %d余り%d", start, enzansi, end, kekka, amari );
exit ( 0 );
}



/*入力された演算子が異なる場合*/
} else {
printf ( "error : '+' '-' '*' '/'のいずれかの演算子を入力して下さい。\n" );
exit ( 0 );
}

/*数値1 演算子 数値2 = 演算結果の形式で出力する */
printf ( "%d %c %d = %d", start, enzansi, end, kekka );
exit ( 0 );
}

もし変な部分がありましたら指摘お願いします。
一応四則演算は問題なくできます。

A 回答 (16件中11~16件)

★追記。


・int が 32 ビット長のとき
 int の範囲は +2147483648 ~ -2147483647 の範囲のため、
>-2147483648 / -1 はエラーになるのです。
 ↑上の数値が -2147483647 を下回るためエラーとなるのです。
・以上。

参考URL:http://www.st.rim.or.jp/~phinloda/cqa/cqa5.html
    • good
    • 0

★チェックのアドバイス


>start = atoi( argv[1] );
>sprintf( str1, "%d", start );

>if ( strcmp(str1,argv[1]) ){
> printf( "error : 整数を入力して下さい。\n" );
> exit( -1 );
>}
・この部分は、strtol 関数でチェックした方が良い。
 strtol 関数ならばエラー検出もできますので、文字列→整数値→文字列の3段階にして
 strcmp 関数で文字列が違うとき整数ではないと判定しなくても良い。
 つまり、
 char *check;
 int start;
 
 start = (int)strtol( argv[1], &check, 10 );
 
 if ( errno != ERANGE ){
  if ( *check != '\0' ){
   printf( "error : 整数を入力して下さい。\n" );
   exit( -1 );
  }
 }
 とすれば整数以外の文字列かどうかをチェックできます。
 end の場合も同じ要領でチェックします。
・詳しくは下の『参考URL』の【使用例】をどうぞ。

その他:
・あと main() 関数がやたらに長いですね。100 行以上もあります。
 (1)引数のチェック部
 (2)オーバーフローのチェック部
 (3)演算部
 (4)表示部
 最低でも4つサブ関数を作成して main() 関数から呼び出すようにしたほうが分かりやすいですよ。
 ソースを今度、書き換えるときは分けてみて下さい。
・以上。参考に!→下の『参考URL』もどうぞ。

参考URL:http://www9.plala.or.jp/sgwr-t/lib/strtol.html
    • good
    • 0
この回答へのお礼

ありがとうございます。関数は思い通りに動くようになったら
やってみたいと思います。

上記strtol関数で、回答者様のコードで試したところ文字を入力すると
その部分が0になってしまいます。どうすればよいのでしょうか?
質問ばかりで申し訳ありません。

お礼日時:2007/05/11 14:03

★64ビットの整数を使用しない場合。


・(1)加算…符号が同じとき (チェック値 > (最大値 - 計算値)) ならオーバーフローが起きる。
 (2)減算…符号が違うとき (チェック値 < (最大値 - 計算値)) ならオーバーフローが起きる。
 (3)乗算…符号に関係なく (チェック値 > (最大値 / 計算値)) ならオーバーフローが起きる。
 (4)除算…割る数が 0 ならば除算エラーが起こる。
  0 で割ると数学上では、無限大(∞)になりオーバーフローが起きる。除算エラーとするか
  オーバーフローさせるかは自由です。
・上記の方法はすべて絶対値でチェックして下さい。
 最大値とは INT_MAX 定数、計算値とは今まで計算した結果の値です。今回は start 値です。
・下にサンプルを載せておきます。

サンプル:
/* 入力された演算子の条件ごとに計算 */
amari = 0; ←0 で初期化

switch ( enzansi ){ ←switch 文で記述してみました。
 case '+':
  if ( ((start > 0) && (end > 0)) || ((start < 0) && (end < 0)) ){
   if ( abs(end) > (max - abs(start)) ){
    /* end を加算するとオーバーフローする */
    exit( -1 );
   }
  }
  kekka = start + end;
  break;
 case '-':
  if ( ((start > 0) && (end < 0)) || ((start < 0) && (end > 0)) ){
   if ( abs(end) > (max - abs(start)) ){
    /* end を減算するとオーバーフローする */
    exit( -1 );
   }
  }
  kekka = start - end;
  break;
 case '*':
  if ( abs(end) > (max / abs(start)) ){
   /* end を乗算するとオーバーフローする */
   exit( -1 );
  }
  kekka = start * end;
  break;
 case '/':
  if ( end == 0 ){
   /* 0 除算エラーです */
   exit( -2 );
  }
  if ( end == -1 ){
   /* Tacosan さんのアドバイスを参考に */
   exit( -1 );
  }
  kekka = start / end;
  amari = start % end; ←ここで計算しておく
  break;
 default:
  /* 演算子エラーなど */
  exit( -3 );
}
if ( amari == 0 ){ ←加算,減算,乗算,割り切れた除算
 printf( "%d %c %d = %d\n", start, enzansi, end, kekka );
}
else{ ←割り切れない除算
 printf( "%d %c %d = %d余り%d\n", start, enzansi, end, kekka, amari );
}

解説:
・足し算は start、end の符号が同じときにオーバーフローのチェックを行っています。
 チェックの『abs(end) > (max - abs(start))』は絶対値でチェックしています。
 『絶対値の end』が『最大値-絶対値の start』よりも大きくなれば、足し算したときに
 max を越えるためここでオーバーフローします。
・引き算は start、end の符号が違うときにオーバーフローのチェックを行っています。
 チェックの『abs(end) > (max - abs(start))』は絶対値でチェックしています。
 『絶対値の end』が『最大値-絶対値の start』よりも大きくなれば、引き算したときに
 max を越えるためここでオーバーフローします。
・乗算も考えは同じですが、符号のチェックは必要ないためカットします。
 あとは足し算と同じチェックの考えですが、引く代わりに割ります。
 つまり『abs(end) > (max / abs(start))』と絶対値でチェックします。
 『絶対値の end』が『最大値÷絶対値の start』よりも大きくなれば、掛け算したときに
 max を越えるためここでオーバーフローします。
・割り算は 0 除算エラーなどをチェックすればよい。あと Tacosan さんのアドバイス通り
 INT_MIN を -1 で割るとき場合によっては INT_MAX を越える可能性があるためチェックを
 入れた方がよいかも。
・以上。参考に!

この回答への補足

一応ソースを書きなおしてみたんですが、startとendに
INT_MAXとINT_MINの数値を超えると
if ( strcmp ( str1, argv[1] ) ) {
printf ( "error : 整数を入力して下さい。\n" );
exit ( -1 );

/*str2とargv[3]による文字列の比較 */
} else if ( strcmp ( str2, argv[3] ) ) {
printf ( "error : 整数を入力して下さい。\n" );
exit ( -1 );
}
この部分でエラーが起きてしまいしまいます。
これはstartとendもMAXとMINを超えないようにするしかないのでしょうか?
文章がわかりにくくてすみません。

#pragma warning ( disable : 4996 )

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

int main ( int argc, char *argv[] )
{
int start;//最初に入力する値(引数1)
int end;//最後に入力する値(引数3)
int kekka = 0;//計算結果を格納
int amari;//除算の余りを格納
int max = INT_MAX;//表すことが出来る最大値
int min = INT_MIN;//表すことが出来る最小値

char enzansi;//入力する演算子 (引数2)
char str1[512];//格納用の配列を宣言(start用)
char str2[512];//格納用の配列を宣言(end用)

/*引数の個数チェック */
if ( argc != 4 ) {
printf ( "usage : %s start enzansi end\n", argv[0] );
exit ( 0 );
}

/*引数を取得して整数型に変換*/
start = atoi ( argv[1] );
end = atoi ( argv[3] );

/*引数の取得*/
enzansi = ( argv[2][0] );

sprintf ( str1, "%d", start );//str1に値を格納
sprintf ( str2, "%d", end );//str2に値を格納

/*str1とargv[1]による文字列の比較 */
if ( strcmp ( str1, argv[1] ) ) {
printf ( "error : 整数を入力して下さい。\n" );
exit ( -1 );

/*str2とargv[3]による文字列の比較 */
} else if ( strcmp ( str2, argv[3] ) ) {
printf ( "error : 整数を入力して下さい。\n" );
exit ( -1 );

/*argv[2]が2文字以上の場合 */
} else if ( strlen ( argv[2] ) >= 2 ) {
printf ( "error : 演算子を正しく入力して下さい。\n" );
exit ( -1 );
}

/*入力された演算子の条件ごとに計算 */
if ( enzansi == '+' ) {
if ( ( ( start > 0 ) && ( end > 0 ) ) || ( ( start < 0 ) && ( end < 0 ) ) ) {
if ( abs ( end ) > ( max - abs ( start ) ) ) {
printf ( "error : オーバーフローがおこります。\n" );
exit( -1 );
}
}

kekka = start + end;

} else if ( enzansi == '-' ) {
if ( ( ( start > 0 ) && ( end < 0 ) ) || ( ( start < 0 ) && ( end > 0 ) ) ) {
if ( abs ( end ) > ( max - abs ( start ) ) ){
printf ( "error : オーバーフローがおこります。\n" );
exit( -1 );
}
}

kekka = start - end;

} else if ( enzansi == '*' ) {
if ( abs ( end ) > ( max / abs ( start ) ) ) {
printf ( "error : オーバーフローがおこります。\n" );
exit( -1 );
}

kekka = start * end;

} else if ( enzansi == '/' ) {

/*startの値を0で除算する場合 */
if ( end == 0 ) {
printf ( "error : 0による除算は禁止です。\n" );
exit ( -1 );
}

if ( kekka = min / -1 ) {
printf ( "error : オーバーフローがおこります。\n" );
exit ( -1 );
}

kekka = start % end;//剰余を求める

/*割り算の結果として余りが出なかった場合 */
if ( kekka == 0 ) {
kekka = start / end;
printf ( "%d %c %d = %d", start, enzansi, end, kekka );
exit ( 0 );

/*割り算の結果として余りが出た場合 */
} else if ( kekka != 0 ) {
kekka = start / end;
amari = start % end;
printf ( "%d %c %d = %d余り%d", start, enzansi, end, kekka, amari );
exit ( 0 );
}

/*入力された演算子が異なる場合*/
} else {
printf ( "error : '+' '-' '*' '/'のいずれかの演算子を入力して下さい。\n" );
exit ( -1 );
}

/*数値1 演算子 数値2 = 演算結果の形式で出力する */
printf ( "%d %c %d = %d", start, enzansi, end, kekka );
exit ( 0 );
}

補足日時:2007/05/11 12:16
    • good
    • 0

#1の回答と同じですが、long long型を使うのが一番簡単です。


ちなみに、Visual C++ 2005でも(.NET 2003でも)long long型を使うことができます。非標準の機能ではありますが、現在では主要な処理系ではlong long型が使えると考えて大丈夫でしょう。

この回答への補足

ありがとうございます。今新人研修中で、上司に
intでやれと言われてしまいました。

補足日時:2007/05/11 12:15
    • good
    • 0

VC++ 2005 だと 64ビットの整数型 (int64 とかそんな感じのやつ) があるので, これを使って #1 のようにするのが簡便でよいのですが, どうしようもなく int だけでやろうとすると, 演算ごとに


・加算においてオーバーフローするのは 2数が同符号のときに限られます. で, a, b > 0 なら b <= INT_MAX - a であること, あるいは a, b < 0 なら b >= INT_MIN + a であることを確認する.
・減算は加算の逆なので似たような感じ.
・乗算は, 「割ったらもとに戻る」でいいのかな?
・除算でオーバーフローするのは INT_MIN / -1 のときに限られます.
のような感じでチェックするしかないような.

この回答への補足

ありがとうございます。INT_MIN / -1は
if ( min / -1 ) {

exit ( 0 );
}
のようなチェックでいいのでしょうか?
この方法だと引数に-2147483648 / -1と入力するとエラーがおこります。
どうすればよいでしょうか?

補足日時:2007/05/11 12:01
    • good
    • 0

処理系依存になりますが、intより大きい型が使用できれば簡単です



int a = 123456789;
int b = 987654321;
long long c = (long long)a * (long long)b;
if (c > INT_MAX) {
printf("オーバーフロー\n");
}

この回答への補足

ありがとうございました。今回はint型を使わなければいけないのです。

補足日時:2007/05/11 11:59
    • good
    • 0

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