
今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 );
}
もし変な部分がありましたら指摘お願いします。
一応四則演算は問題なくできます。
No.14ベストアンサー
- 回答日時:
★Werner さん、そうですね。
注意書きするよりは goto を使うべきでしたね。・それで追記としてサブ関数にした方が見やすいと付けましたが言葉が足りませんでしたね。
質問者さんへ。main() 関数の for(;;) の部分がトリッキーでしたので書き直してみました。
エラー表示の switch 文を void error_print(int errno); というサブ関数にしました。
// エラー表示
void error_print( int errno )
{
char *errmsg; // エラー文字列を格納
switch ( errno ){
case ERR_USAGE:
errmsg = "usage : %s start enzansi end";
break;
case ERR_INTEGER:
errmsg = "整数を入力して下さい。";
break;
case ERR_INTOVER:
errmsg = "整数型の範囲を超えました。";
break;
case ERR_ENZAN:
errmsg = "演算子を正しく入力して下さい。";
break;
case ERR_DIVID:
errmsg = "0による除算は禁止です。";
break;
case ERR_OVERFLOW:
errmsg = "オーバーフローが起こります。";
break;
}
puts( errmsg );
}
// main 関数
int main( int argc, char *argv[] )
{
int kekka; // 計算結果を格納
int ret; // 戻り値を受け取る
/* (1)引数のチェック */
if ( (ret = param_check(argc,argv)) != ERR_SUCCESS ){
error_print( ret ); ←ここを関数にした。
return ret;
}
/* (2)オーバーフローのチェック */
if ( (ret = overflow_check(INT_MIN,INT_MAX)) != ERR_SUCCESS ){
error_print( ret ); ←ここを関数にした。
return ret;
}
/* (3)演算子による四則演算 */
kekka = calc( start, enzansi, end );
/* (4)演算結果を表示 */
printf ( "%d %c %d = %d", start, enzansi, end, kekka );
return 0;
}
その他:
・引き算のオーバーフローのロジックを整理すると
(1)正数 - 正数
(2)正数 - 負数
(3)負数 - 正数
(4)負数 - 負数
の4タイプしかありません。
このうち、(1)、(4)は int 型の範囲に納まるためチェックは不要です。
あとは(2)、(3)の符号が異なるときに、それぞれオーバーフローのチェックを分けて
記述すれば良い。詳しくは回答者 No.13 さんのアドバイスをどうぞ。
・以上。
ありがとうございました。すみません。
いろいろごっちゃになってきたので、まず、オーバーフローだけに
ついて、完璧にしたいと思います。absを使わない方が良いのでしょうか?
なぜか掛け算でもおかしくなるし、引き算もまだ完璧ではありません。
お忙しいでしょうが、よろしくお願いします。
No.16
- 回答日時:
> -2147483648 - -1もオーバーフローになってしまいました。
どうすればよいのでしょうか?デバッグしてください。
おかしくなる条件が分かっているのだから、
チェック過程をprintfで出力するとかしてトレースしてみましょう。
(ちょっと流れを見ていて気になったのですが、人に頼りすぎてませんか?
これはあなたの研修課題で、本来自分で解決すべきなんですよね?
だったらあまり他人に頼りすぎるのは良くないんじゃないかなあ…。
自分で十分考えて分からなかったので尋ねているというならまあいいんじゃないかとは思いますが。
しかし、この文おせっかい以外の何物でもないな^^;)
まあ私がANo.13で説明したとおりなら、AもBも負の時は、
> if(Aは正、Bは負)
> if(Aは負、Bは正)
のどちらでもないからifブロックの中は実行されずオーバーフロー扱いにはならないはずで、
私からはANo.13で説明したとおりに書き直してくださいとしか言えないんですけどね。
それから、私もサンプルコードを書いてみたので載せておきます。
(上で自分で考えろみたいなことを言っておいて載せるのもどうかと思うけど、
せっかく書いたので^^;)
----------------------------------------------------------------------
#include <stdio.h>
#include <limits.h>
enum OP {
ADD, SUB, MUL, DIV
};
/* @return 正常時0、overflow時1 */
int is_overflowed(enum OP op, int operand1, int operand2){
switch(op){
case ADD:
if(( (operand1>0 && operand2>0) && (operand1>INT_MAX-operand2) )
|| ( (operand1<0 && operand2<0) && (operand1<INT_MIN-operand2) )){
return 1;
}
break;
case SUB:
if(( (operand1>0 && operand2<0) && (operand1>INT_MAX+operand2) )
|| ( (operand1<0 && operand2>0) && (operand1<INT_MIN+operand2) )){
return 1;
}
break;
case MUL:
/* チェック時のINT_MIN/-1を避けるために先に確認 */
if( operand2==-1 ){
if(operand1 == INT_MIN) return 1;
else return 0;
}
/* チェック時の0除算に注意、operand2==0の時は除算してはいけない */
if(( operand2>0 && (operand1>INT_MAX/operand2 || operand1<INT_MIN/operand2) )
|| ( operand2<0 && (operand1<INT_MAX/operand2 || operand1>INT_MIN/operand2) )){
return 1;
}
break;
case DIV:
if(operand2==-1 && operand1 == INT_MIN){
return 1;
}
break;
default:
return 2;
break;
}
return 0;
}
/* チェック結果表示するためのラッパー関数 */
void check_overflow(enum OP op, int operand1, int operand2){
int ret_val;
ret_val = is_overflowed(op, operand1, operand2);
if(ret_val == 0){
printf("no error: %d %c %d\n", operand1, "+-*/"[op], operand2);
}
else if(ret_val == 1){
printf("overflow occurred: %d %c %d\n", operand1, "+-*/"[op], operand2);
}
}
int main(void){
check_overflow(ADD , INT_MAX, 1);
check_overflow(ADD , INT_MAX, -1);
check_overflow(ADD , INT_MIN, 1);
check_overflow(ADD , INT_MIN, -1);
check_overflow(SUB , INT_MAX, 1);
check_overflow(SUB , INT_MAX, -1);
check_overflow(SUB , INT_MIN, 1);
check_overflow(SUB , INT_MIN, -1);
check_overflow(MUL , INT_MAX/2, 2);
check_overflow(MUL , INT_MAX/2+1, 2);
check_overflow(MUL , -(INT_MIN/2), -2);
check_overflow(MUL , -(INT_MIN/2)+1, -2);
check_overflow(MUL , INT_MIN/2, 2);
check_overflow(MUL , INT_MIN/2-1, 2);
check_overflow(MUL , -(INT_MAX/2), -2);
check_overflow(MUL , -(INT_MAX/2)-1, -2);
check_overflow(MUL , INT_MAX, -1);
check_overflow(MUL , INT_MIN, 1);
check_overflow(MUL , INT_MIN, -1);
/*オペランドを左右交換*/
check_overflow(MUL , 2, INT_MAX/2);
check_overflow(MUL , 2, INT_MAX/2+1);
check_overflow(MUL , -2, -(INT_MIN/2));
check_overflow(MUL , -2, -(INT_MIN/2)+1);
check_overflow(MUL , 2, INT_MIN/2);
check_overflow(MUL , 2, INT_MIN/2-1);
check_overflow(MUL , -2, -(INT_MAX/2));
check_overflow(MUL , -2, -(INT_MAX/2)-1);
check_overflow(MUL , -1, INT_MAX);
check_overflow(MUL , 1, INT_MIN);
check_overflow(MUL , -1, INT_MIN);
check_overflow(DIV , INT_MAX, -1);
check_overflow(DIV , INT_MIN, -1);
return 0;
}
この回答への補足
> if(Aは正、Bは負)
> if(Aは負、Bは正)
のとおりに条件指定したのですが、-2147483648 - 1とすると、
なぜか符号が変わり、2147483647という結果になってしまいました。
ちなみにオーバーフローのロジックは決まっているということですが、
なぜそうなるのかの理由が分かりません。教えていただけないでしょうか?
No.15
- 回答日時:
流れを読まずにぺたり。
#include <stdio.h>
#include <limits.h>
#ifdef DEBUG
#define debug_printf(ARG) fprintf(stderr, ARG);
#else
#define debug_printf(ARG)
#endif
enum OP {
ADD, SUB, MUL, DIV
};
struct CALCDATA {
enum OP op;
int operand1;
int operand2;
};
struct CALCDATA data[] = {
{ADD, INT_MAX, 1},
{ADD, INT_MAX, 0},
{ADD, INT_MAX-1, 1},
{ADD, -1, 2},
{SUB, INT_MIN, 1},
{SUB, INT_MIN, 0},
{SUB, INT_MIN+1, 1},
{SUB, 2, 3},
{MUL, SHRT_MAX+1, SHRT_MAX+1},
{MUL, SHRT_MAX+1, SHRT_MAX},
{MUL, INT_MIN-1, 3},
{MUL, INT_MIN/2, 2},
{MUL, INT_MIN/2+1, 2},
{MUL, INT_MIN/2, 3},
{MUL, INT_MIN/4, 3},
{DIV, INT_MIN, -1},
};
#define INT_BIT (sizeof (int) * CHAR_BIT)
#define MSB (1 << (INT_BIT-1))
unsigned int
ones(unsigned int x)
{
x -= ((x >> 1) & 0x55555555);
x = (((x >> 2) & 0x33333333) + (x & 0x33333333));
x = (((x >> 4) + x) & 0x0f0f0f0f);
x += (x >> 8);
x += (x >> 16);
return(x & 0x0000003f);
}
unsigned int
lzc(unsigned int x)
{
x |= (x >> 1);
x |= (x >> 2);
x |= (x >> 4);
x |= (x >> 8);
x |= (x >> 16);
return(INT_BIT - ones(x));
}
int
is_overflowed(struct CALCDATA d)
{
int d1, d2, d3, d4;
d1 = d.operand1;
d2 = d.operand2;
d3 = 0;
d4 = 0;
switch (d.op) {
case ADD:
debug_printf(("### %d + %d\n", d1, d2));
if (d1==0 || d2==0)
return 0;
if ( (d3=(d1 & MSB) ^ (d2 & MSB))
^ (((d1+d2) & MSB) ^ d3))
return 1;
break;
case SUB:
debug_printf(("### %d - %d\n", d1, d2));
debug_printf(("\td3=%x\n", !((d1 & MSB) ^ (d2 & MSB))));
debug_printf(("\texpr & MSB=%x\n", ((d1-d2)&MSB)));
if (d1==0 || d2==0)
return 0;
if ((!(d1 & MSB) && (d2 & MSB))
|| ((d1 & MSB) && !(d2 & MSB))) {
d3 = (d1 - d2) & MSB;
if (d3 ^ (d1 & MSB))
return 1;
}
break;
case MUL:
debug_printf(("### %d * %d\n", d1, d2));
if (d1 == 0 || d2 == 0)
return 0;
if ((d1 == INT_MIN && d2 != 0)
|| (d1 != 0 && d2 == INT_MIN))
return 1;
d3 = (d1 ^ d2) & MSB;
if (d1 < 0)
d1 = ~d1 + 1;
if (d2 < 0)
d2 = ~d2 + 1;
debug_printf(("\t%d * %d (%x)\n", d1, d2, d3));
debug_printf(("\tlzc(d1)=%d * lzc(d2)=%d\n", lzc(d1), lzc(d2)));
d4 = lzc(d1) + lzc(d2);
if (d4 > INT_BIT)
return 0;
if (!(d1 & (d1-1)) && !(d2 & (d2-1))) {
if (d4 == INT_BIT)
return 1;
return 0;
}
if (d4 < INT_BIT)
return 1;
break;
case DIV:
debug_printf(("### %d / %d\n", d1, d2));
if (d1 == INT_MIN && d2 == -1)
return 1;
break;
}
return 0;
}
int
main()
{
int i;
for (i=0; i<(sizeof data/sizeof data[0]); i++) {
printf("Try %d %c %d ",
data[i].operand1,
data[i].op["+-*/"],
data[i].operand2);
if (is_overflowed(data[i]))
printf("overflow!");
putchar('\n');
}
}
No.13
- 回答日時:
> ちなみに-2147483647 - 1が-2147483648とならずにオーバーフローとなります。
これはどうしようもないのでしょうか?> 掛け算、引き算部分のソースでおかしい部分があるのでしょうか?
以下の部分で、
> /* オーバーフローのチェック */
> if ( ( ( start > 0 ) && ( end < 0 ) ) || ( ( start < 0 ) && ( end > 0 ) ) ) {
> if ( abs ( end ) > ( max - abs ( start ) ) ) {
> printf ( "error : オーバーフローが起こります。\n" );
> exit( -1 );
> }
> }
正-負と負-正をまとめて処理しているのが原因でしょう。
abs使わずにちゃんと場合分けしてみてください。
/* A-Bのオーバーフローチェック */
if(Aは正、Bは負){
/* A-B>INT_MAXでオーバーフロー発生
チェックするためにオーバーフローが起こらない形に式変形*/
if(A>INT_MAX+B){/*Err*/}
}
if(Aは負、Bは正){
/* A-B<INT_MINでオーバーフロー発生 */
if(A<INT_MIN+B){/*Err*/}
}
そもそも、startやendがINT_MINで有る可能性がある以上absは使うべきではないと思います。
(abs(INT_MIN)がINT_MAXを超えて表現できなくなるから。
私の環境で試してみたら、abs(INT_MIN)==INT_MINになってしまいました。)
なので、同じくabsを使っている加算・乗算部分も書き換えた方がよいでしょう。
あと今更だけど、startやendという変数名は分かりにくい気がしました。
startやendはただのオペランドで、始まるとか終わるという意味は持ってないですよね?
これならまだaとbとかの方が良いと個人的には思います。
a、bが味気ないなら、左オペランド、右オペランドのニュアンスを持たせるとか。
(命名のしかたはかなり個人の好みの要素が入るので、あまりこの部分は真剣に考えてくれなくてかまいませんが)
> ANo.11
> for ( ; ; ){ ←繰り返し文としては使っていない。注意!
そういう注釈が必要な使い方をするくらいなら、
goto ERROR;
とかにした方が良くないですか?
ありがとうございました。
if(Aは正、Bは負){
/* A-B>INT_MAXでオーバーフロー発生
チェックするためにオーバーフローが起こらない形に式変形*/
if(A>INT_MAX+B){/*Err*/}
}
if(Aは負、Bは正){
/* A-B<INT_MINでオーバーフロー発生 */
if(A<INT_MIN+B){/*Err*/}
}
というふうにしたら、-2147483648 - 1はオーバーフローのメッセージが
表示されて、成功したのですが、-2147483648 - -1もオーバーフローになってしまいました。どうすればよいのでしょうか?
No.11
- 回答日時:
★サブ関数の役割を整理して分割すればよい。
(1)引数のチェック(start,end,enzansi,strtol でのエラーチェックです)
(2)演算子によるオーバーフローのチェック(加算,減算,乗算,除算で戻り値にチェック結果を返す)
(3)演算子による四則演算の処理(ここではオーバーフローのチェックはなくてよい)
(4)演算結果を表示…これは関数にしなくても良いかも。→main() で表示すれば良い。
・大きく分けて上記の4つの自作関数を作ります。
また、エラーが起きたときや、オーバーフローの printf() 文は main() 関数で記述する方が
すっきりします。よって、サブ関数では printf() は基本的に使わずに作ります。
・下に main() 関数のサンプルを載せます。
サンプル:
/* エラー定数の定義 */
#define ERR_SUCCESS (0) // 正常
#define ERR_USAGE (-1) // usage : %s start enzansi end
#define ERR_INTEGER (-2) // 整数を入力して下さい。
#define ERR_INTOVER (-3) // 整数型の範囲を超えました。
#define ERR_ENZAN (-4) // 演算子を正しく入力して下さい。
#define ERR_DIVID (-5) // 0による除算は禁止です。
#define ERR_OVERFLOW (-6) // オーバーフローが起こります。
/* 変数をグローバルで定義 */
int start; // 最初に入力する値
int end; // 最後に入力する値
int amari; // 除算の余りを格納
char enzansi; // 入力する演算子
/* 関数のプロトタイプ宣言 */
int param_check( int argc, char *argv[] );
int overflow_check( int min, int max );
int calc( int start, int enzansi, int end );
int main( int argc, char *argv[] )
{
char *errmsg; // エラー文字列を格納
int kekka; // 計算結果を格納
int ret; // 戻り値を受け取る
for ( ; ; ){ ←繰り返し文としては使っていない。注意!
/* (1)引数のチェック */
if ( (ret = param_check(argc,argv)) != ERR_SUCCESS ){
break; ←エラーが発生したので for 文を抜ける
}
/* (2)オーバーフローのチェック */
if ( (ret = overflow_check(INT_MIN,INT_MAX)) != ERR_SUCCESS ){
break; ←エラーが発生したので for 文を抜ける
}
/* (3)演算子による四則演算 */
kekka = calc( start, enzansi, end );
/* (4)演算結果を表示 */
printf ( "%d %c %d = %d", start, enzansi, end, kekka );
return 0;
}
switch ( ret ){
case ERR_USAGE:
errmsg = "usage : %s start enzansi end";
break;
case ERR_INTEGER:
errmsg = "整数を入力して下さい。";
break;
case ERR_INTOVER:
errmsg = "整数型の範囲を超えました。";
break;
case ERR_ENZAN:
errmsg = "演算子を正しく入力して下さい。";
break;
case ERR_DIVID:
errmsg = "0による除算は禁止です。";
break;
case ERR_OVERFLOW:
errmsg = "オーバーフローが起こります。";
break;
}
puts( errmsg );
return ret;
}
その他:
・main 関数だけを紹介しました。
あとはサブ関数の param_check()、overflow_check()、calc() を作成して下さい。
param_check() は引数の個数と strtol() で start、end に整数にセットします。
この関数では ERR_INTEGER、ERR_INTOVER、ERR_ENZAN を返すように作ります。
すべてチェックが正常ならば ERR_SUCCESS 定数を返す。
・overflow_check() では演算子によるオーバーフローをすべてチェックして
オーバーフローが起きる可能性があれば ERR_OVERFLOW を返し、0 で除算しようと
した場合は ERR_DIVID を返すようにします。また、オーバーフローしなければ
正常なので ERR_SUCCESS 定数を返す。
それから最小値、最大値を引数で受け取れるようにしておきます。
こうしておけば、main() 関数の overflow_check() 部分で上限や下限を簡単に変更できます。
・calc() では、引数から start、enzansi、end を受け取って四則演算を行います。
オーバーフローなどのエラーはすべて overflow_check() で行っているため、この関数では
エラーなしで単純な四則演算をすれば良いだけです。
switch 文を使えば見やすくなります。もちろん、if、else if、else 構文でも構いません。
最後に:
・グローバル変数にしている start、end、amari、enzansi は1つの構造体で管理すれば
もっと分かりやすくなります。構造体が分かるのならば、うまく書き換えてみて下さい。
・以上。参考に。
お世話になっております。
構造体はまだわかりません(泣)
ユーザー定義関数...勉強してみます。
ちなみに、上司に見てもらったら
「-2147483648 × 1 = オーバーフローが起こります。」
「-2147483648 - 1 = 2147483647」
の結果がおかしいことを申告されてしまいました。
確かに上は-2147483648にならなきゃおかしいし、下はオーバーフローにならないといけないんですよね?
掛け算、引き算部分のソースでおかしい部分があるのでしょうか?
No.10
- 回答日時:
★110行、付近にある最後の『オーバーフロー』チェックがおかしいから。
>/* オーバーフローのチェック */
>if ( min / -1 ) {
> printf ( "error4 : オーバーフローが起こります。\n" );
> exit ( -1 );
>}
・の部分の if 判定が間違っています。
間違い⇒if ( min / -1 ){
正しい⇒if ( (start == min) && (end == -1) ){
これで -2147483648 / 1 は正しく計算されます。
また、-2147483648 / -1 はオーバーフローとして表示されます。
・あとはサブ関数に分離して分かりやすく main() 関数を書き直すだけですね。
以上。頑張って下さい。
あぁ本当にありがとうございました。
お礼に100ポイントくらいあげたいです(笑)
後はしっかり意味を理解できるようにしたいと思います。
ちなみに-2147483647 - 1が-2147483648とならずにオーバーフローとなります。これはどうしようもないのでしょうか?
お忙しい中本当に申し訳ありません。
No.9
- 回答日時:
★エラーメッセージが出なくて正しいと思います。
・マイナス値(-2147483648)に +120 を足しているのですよ。
ですから加算結果の -2147483528 は int 型の範囲である -2147483648 ~ +2147483647 に
納まります。オーバーフローにはなりませんよ。
・あと int 型の範囲 -2147483648~ +2147483647 を超えた数字を start もしくは end に入力
してしまうと、強制的に結果が最大値になります。strtol() 関数の仕組みです。
解決策:
char *check;
int start;
start = (int)strtol( argv[1], &check, 10 );
if ( errno != ERANGE ){
if ( *check != '\0' ){
printf( "error : 整数を入力して下さい。\n" );
exit( -1 );
}
}
else{
printf( "error : 整数型の範囲を超えました。\n" );
exit( -1 );
}
↑この部分を追加すればよい。
・以上。
この回答への補足
#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;//最初に入力する値
int end;//最後に入力する値
int kekka;//計算結果を格納
int amari;//除算の余りを格納
int max = INT_MAX;//表すことが出来る最大値
int min = INT_MIN;//表すことが出来る最小値
char *check;
char enzansi;// 入力する演算子
/*引数の個数チェック */
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] );
/*startが整数であるか文字であるかのチェック */
start = ( int ) strtol ( argv[1], &check, 10 );
if ( errno != ERANGE ) {
if ( *check != '\0' ) {
printf ( "error : 整数を入力して下さい。\n" );
exit ( -1 );
}
} else {
printf( "error : 整数型の範囲を超えました。\n" );
exit( -1 );
}
/*endが整数であるか文字であるかのチェック */
end = ( int ) strtol ( argv[3], &check, 10 );
if ( errno != ERANGE ) {
if ( *check != '\0' ) {
printf ( "error : 整数を入力して下さい。\n" );
exit ( -1 );
}
} else {
printf( "error : 整数型の範囲を超えました。\n" );
exit( -1 );
}
/*argv[2]が2文字以上の場合 */
if ( strlen ( argv[2] ) >= 2 ) {
printf ( "error : 演算子を正しく入力して下さい。\n" );
exit ( -1 );
}
/*argv[2]に入力された演算子が'+'の場合 */
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;
/*argv[2]に入力された演算子が'-'の場合 */
} 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;
/*argv[2]に入力された演算子が'*'の場合 */
} else if ( enzansi == '*' ) {
/*オーバーフローのチェック */
if ( abs ( end ) > ( max / abs ( start ) ) ) {
printf ( "error : オーバーフローが起こります。\n" );
exit( -1 );
}
kekka = start * end;
/*argv[2]に入力された演算子が'/'の場合 */
} else if ( enzansi == '/' ) {
/*startの値を0で除算する場合 */
if ( end == 0 ) {
printf ( "error : 0による除算は禁止です。\n" );
exit ( -1 );
}
/*オーバーフローのチェック */
if ( 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 );
}
何度も本当にありがとうございます。Oh-Orangeさんに回答して
いただいたおかげで本当に助かりました。しかしまだ少し問題が...
-2147483648 / 1 などとすると、答えは-2147483648となり、範囲内に収まっているハズなのですが、なぜかオーバーフローです。とメッセージが出ます。同様に-2147483647 - 1等としても答えは上記になると思うのですが、これもオーバーフローです。とメッセージが出ます。以下にソースを記載するのでおかしい部分を指摘していただけないでしょうか?
かなり見ずらいと思いますが、よろしくお願いします。
No.8
- 回答日時:
strtol が使えるなら, 変換後 errno == ERANGE のときにオーバーフローしてるみたい.
この回答への補足
こちらから失礼します。サブ関数とは自作関数のことですよね?
いろいろ分けて作ろうとしたら訳がわからなくなってしまいました。
具体的にはどのように作成すればよいのでしょうか?
int main ( int argc, char *argv[] )
{
int start;//最初に入力する値
int end;//最後に入力する値
int kekka;//計算結果を格納
int amari;//除算の余りを格納
int max = INT_MAX;//表すことが出来る最大値
int min = INT_MIN;//表すことが出来る最小値
char *check;//文字列中に変換不可能な文字があった場合の格納場所
char enzansi;// 入力する演算子
/*引数の個数チェック */
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] );
/*startが整数であるか文字であるかのチェック */
start = strtol ( argv[1], &check, 10 );
if ( errno != ERANGE ) {
if ( *check != '\0' ) {
printf ( "error : 整数を入力して下さい。\n" );
exit ( -1 );
}
/*startに範囲を超えた値を入力した場合*/
} else {
printf( "error : 整数型の範囲を超えました。\n" );
exit( -1 );
}
/*endが整数であるか文字であるかのチェック */
end = strtol ( argv[3], &check, 10 );
if ( errno != ERANGE ) {
if ( *check != '\0' ) {
printf ( "error : 整数を入力して下さい。\n" );
exit ( -1 );
}
/*endに範囲を超えた値を入力した場合 */
} else {
printf( "error : 整数型の範囲を超えました。\n" );
exit( -1 );
}
/*argv[2]が2文字以上の場合 */
if ( strlen ( argv[2] ) >= 2 ) {
printf ( "error : 演算子を正しく入力して下さい。\n" );
exit ( -1 );
}
/*argv[2]に入力された演算子が'+'の場合 */
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 ;
/*argv[2]に入力された演算子が'-'の場合 */
} 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;
/*argv[2]に入力された演算子が'*'の場合 */
} else if ( enzansi == '*' ) {
/*オーバーフローのチェック */
if ( abs ( end ) > ( max / abs ( start ) ) ) {
printf ( "error : オーバーフローが起こります。\n" );
exit( -1 );
}
kekka = start * end;
/*argv[2]に入力された演算子が'/'の場合 */
} else if ( enzansi == '/' ) {
/*startの値を0で除算する場合 */
if ( end == 0 ) {
printf ( "error : 0による除算は禁止です。\n" );
exit ( -1 );
}
/*オーバーフローのチェック */
if ( ( start == min ) && ( end == -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 );
}
No.7
- 回答日時:
★修正⇒2147483648 と 2147483647 が反対でした。
・間違い⇒int の範囲は +2147483648 ~ -2147483647 の範囲のため…
正しい⇒int の範囲は +2147483647 ~ -2147483648 の範囲のため…
それで -2147483648 / -1 という式は、エラーというよりは警告メッセージですよね。
(INT_MIN / -1) なら警告メッセージは出ませんでした。
・また、(INT_MIN / -1) の結果が INT_MAX を越えると 0 に戻ってしまいますので
オーバーフローするように処理すれば良い。
・以上。
この回答への補足
例えば引数を-214748364000000 + 120などとすると、
-2147483648 + 120 = 2147483528等となってしまい、エラーメッセージが
表示されません。どうすればよいのでしょうか?
即レスありがとうございます。なんとか出来ました。最後の質問なのですが、
-2147483648~+2147483647を超えた数字をstartもしくはendに入力してしまうと、強制的に結果が上記の数字になってしまうのですが、どうすれば直るのでしょうか?もしくはそれは無理なのでしょうか?
よろしくお願いします。
お探しのQ&Aが見つからない時は、教えて!gooで質問しましょう!
似たような質問が見つかりました
- C言語・C++・C# プログラミングの授業の課題です 1 2023/01/17 22:15
- C言語・C++・C# バイナリファイルをコピーするのにかかる時間を測りたいのですが実行するとFatel error:gli 2 2022/11/03 01:10
- C言語・C++・C# 並列プログラミングのπ計算について 1 2022/07/16 22:30
- C言語・C++・C# c言語 プログラムのエラー 1 2023/02/11 20:31
- C言語・C++・C# c言語の問題の説明、各所ごとに 5 2023/07/26 11:03
- C言語・C++・C# c言語の問題です 3 2023/01/10 16:15
- PHP PHP MySql ページング 2 2022/09/20 06:38
- C言語・C++・C# C言語のエラーについて 2 2022/07/11 13:56
- C言語・C++・C# C言語 共用体について コマンドライン引数で値を2つ入力したときに、argv[2]の値をUNI u1 4 2022/04/25 20:34
- C言語・C++・C# このプログラミング誰か教えてくれませんか 1 2022/06/02 15:27
関連するカテゴリからQ&Aを探す
おすすめ情報
デイリーランキングこのカテゴリの人気デイリーQ&Aランキング
-
doubleからintへのキャスト
-
C言語 コンパイルエラー(文字...
-
C言語:2つの複素数(分数)の四...
-
このどこにバグがあるか、教え...
-
TCP/IP: 非ソケットに対するソ...
-
コマンドライン引数について。
-
分割コンパイルの手順と方法に...
-
リターンキー又は、スペースキ...
-
C言語 文字列の中の文字が条件...
-
複数のシリンダーの同調化にコ...
-
クレーンでのCFブレーキとな...
-
互いに素と負の数
-
PID制御
-
RCフィルタと三角波、方形波
-
数学Aの問題です。 答えは、左...
-
16進数を正負反転する方法
-
SPI 非言語の問題です
-
整合フィルタ(matched filter)...
-
定常応答 と 強制応答 は同...
-
主増幅器と前置増幅器について
マンスリーランキングこのカテゴリの人気マンスリーQ&Aランキング
-
Expression: nptr != NULL
-
リターンキー又は、スペースキ...
-
getch / putch用のヘッダ
-
TCP/IP: 非ソケットに対するソ...
-
アセンブリプログラムで特定の...
-
C言語プログラミングで・・・
-
C言語のエラーについて
-
'printf':識別子が見つかりませ...
-
C言語 コンパイルエラー(文字...
-
c言語プログラミングで初項を1....
-
c言語についての質問です。
-
加減剰余のオーバーフローについて
-
最も文字数が多い行番号と文字...
-
コンソールAPIのSetConsoleScre...
-
C言語で自作ヘッダーを作ったの...
-
プログラミングが分かりません。
-
C言語のソースコードについて教...
-
クレーンでのCFブレーキとな...
-
SPI 非言語の問題です
-
複数のシリンダーの同調化にコ...
おすすめ情報