- ベストアンサー
LONG_MINを使って、正常値かどうか確かめたい。
こんにちは。30代の男性です。 C言語で、コマンドラインオプションから入力された2つの数字がlong型の範囲外であれば「正常値ではない。」と表示され、適正値なら加算が行われるプログラムを作っています。LONG_MINやLONG_MAXは正常値の範囲外の値かと思っていましたが、それぞれlong型の最小値と最大値なのですね。 -2147483648を入力しても正常に計算が行われ、-2147483649が入力されるとエラーが出されるようにするにはどのようにしたらよいでしょうか? よろしくお願いいたします。
- みんなの回答 (5)
- 専門家の回答
質問者が選んだベストアンサー
★自作関数でチェックする場合を紹介します。 ・最初に ASCII コードの環境のみです。→この環境に限定。 サンプル: int MyCheckAndStrtol( long *ret, const char *string ) { unsigned long z; unsigned long n; int sign = +1; ←符号がない場合は『+』 while ( isspace(*string) ){ ←空白文字はカット string++; } if ( *string == '+' ){ sign *= +1; string++; } else if ( *string == '-' ){ sign *= -1; string++; } else{ return( -1 ); ←符号以外の文字エラー } for ( n = 0L ; isdigit(*string) ; string++ ){ z = n; n *= 10L; n += (*string - '0'); ←文字を数値に変換 if ( (n / 10L) != z ){ ←オーバーフロー時のチェック return( 0 ); } } if ( *string != '\0' ){ ←この処理はなくても良いかも。チェックの仕様により自由です。 return( -1 ); ←文字列の最後が \0 以外ならばエラーとする。カットしてもOK。 } *ret = (long)(sign * n); return( +1 ); ←範囲内。 } 最後に: ・サンプル『MyCheckAndStrtol』関数の引数は、 第1引数は文字列を整数に変換した値をセットします。→long型へのポインタ 第2引数はチェックする文字列です。→コマンドラインの引数などを渡す ・戻り値は、 0以上は long 型の範囲内。→正常値。 0ならば long 型の範囲外。→異常値。 0以下は、整数文字列としては記述がおかしいエラー。→『abc』などの文字 ・以上。おわり。→細かくチェックするには文字列を解析するしかないかもね。
その他の回答 (4)
- jacta
- ベストアンサー率26% (845/3158)
既に回答が出ているように、strtolを使えばエラー検出が可能です(これが、atoiやatolを使うべきではない理由でもあります)。 簡単に例を挙げると、 #include <stdlib.h> #include <errno.h> int main(int argc, char *argv[]) { char *endptr; long value; errno = 0; value = strtol(argv[1], &endptr, 0); if (errno != 0 || *endptr != '\0') { /* エラー処理 */ } ... return 0; } といった感じです。 ここで、strtolの第3引数(基数)を0にしているのは、0で始まれば8進数、0xまたは0Xで始まれば16進数として解釈できるようにするためです。 なお、long long型が使える処理系でも、-2147483648と比較するのはお勧めできません。移植性のこともありますが、long型が32ビットの場合、-2147483648はunsigned long型になってしまうため、long long型に型変換しても負の値にならないためです。C99であれば、-2147483648はlong long型になるので問題ありません。
お礼
ご回答頂きありがとうございます。 アドバイスも参考にさせて頂きます。ありがとうございました。
- venzou
- ベストアンサー率71% (311/435)
こんな感じで、調べられます。 #include <stdio.h> #include <stdlib.h> #include <limits.h> void main(int argc, char *argv[]) { int i; char *e; long n; for(i = 1; i < argc; i++){ n = strtol(argv[i], &e, 10); if(errno != ERANGE){ if (*e == '\0'){ printf("%02d : 値 = %ld\n", i, n); }else{ printf("%02d : 値 = %ld 変換不能部分(%s)\n", i, n, e); } }else if (n == LONG_MAX){ printf("%02d : 大きすぎ (%s)\n", i, argv[i]); }else if (n == LONG_MIN){ printf("%02d : 小さすぎ (%s)\n", i, argv[i]); } } } (注:見やすいように全角スペースが入っています、コンパイルする時は消してください。)
お礼
ご回答ありがとうございました。 Unixでコンパイルしましたが、エラーが出ました(^-^;。全角は削除したのですけどね。でも、もう解決できたので大丈夫です。 ありがとうございました。
- sakusaker7
- ベストアンサー率62% (800/1280)
コマンドラインで入力したものをチェックするということですので、 文字列→数値(long)への変換をしているということですよね。 であれば、strtol を使えばlongのオーバーフローが あるかどうかは検出できると思いますが? Manpage of STRTOL http://www.linux.or.jp/JM/html/LDP_man-pages/man3/strtol.3.html アンダーフローもオーバーフローも起きなかった場合、strtol() 関数は変換された値を返す。オーバーフローした場合には LONG_MAX が返り、アンダーフローした場合には LONG_MIN が返る。オーバーフロー、アンダーフローのいずれの場合にも大域変数 errno には ERANGE が設定される。 strtoll() も同様であるが、LONG_MIN と LONG_MAX の代わりに LLONG_MIN と LLONG_MAX が返される。
お礼
ご回答ありがとうございます。 >オーバーフローした場合には LONG_MAX が返り、アンダーフローした場合には LONG_MIN が返る。 LONG_XXXが返ってくるだけで計算は続行されてしまいたが、errnoが検出されたかどうかをif文で使ったら、うまく処理を分岐させることができました。 ありがとうございました。
- Oh-Orange
- ベストアンサー率63% (854/1345)
★最初に long 型でのチェックは出来ません。 ・同じ long 型では範囲を超えるためにチェックできません。 そこで『labs』というlong 型の絶対値を返す関数を利用します。 この関数で絶対値が返されたらば、一度『unsigned long』型の変数に代入します。 ・そして、その『unsigned long』型の変数で『LONG_MAX』よりも大きければエラーとします。 なお、『正数』の場合は『LONG_MAX - 1』を超えたらばエラーとします。負数の場合は 『LONG_MAX』以上ならエラーとしますが…。 ・これは long 型の範囲が『-2147483648』~『+2147483647』だからです。分かりますか? サンプル: unsigned long check; long value; ←これがチェックする long 型 check = (unsigned long)labs( value ); if ( value < 0 ){ if ( check > LONG_MAX ){ return( ERR ); } } else{ if ( check >= LONG_MAX ){ return( ERR ); } } 最後に: ・チェックする値の符号により次のようになります。 正数⇒最大『2147483647』まで正常値、『2147483648』以上はエラーです。 負数⇒最大『2147483648』まで正常値、『2147483649』以上はエラーです。 ・注意点としては、long 型よりも大きい数値(桁数で9桁以上など)は当然チェックできません。 この場合は、double 型に代入してチェックしますが、double 型よりも大きい数値を渡すと またまたチェックできなくなります。どこまでチェックしますか? ・以上。おわり。→チェック範囲が『unsigned long』型に収まるならば上記のサンプルで可能。
お礼
Oh-Orange様 ご回答頂きありがとうございます。私はOh-Orange様にはたびたびお世話になっており、大変ありがたく思っています。 今後も宜しくお願い致します。
お礼
Oh-Orange様 わざわざサンプルコードをお送り頂き、ありがとうございます。問題は解決でました。ありがとうございました。