5

最近、コードにバグが見つかり、デバッグに数時間かかりました。

問題は、次のように定義された関数にありました。

unsigned int foo(unsigned int i){
   long int v[]={i-1,i,i+1} ;
       .
       .
       .
 return x ; // evaluated by the function but not essential how for this problem.
}

vの定義は、私の開発マシン(ubuntu 12.04 32ビット、g ++コンパイラ)では問題を引き起こしませんでした。この場合、unsignedintは暗黙的にlongintに変換され、負の値は正しく処理されました。

別のマシン(ubuntu 12.04 64ビット、g ++コンパイラ)では、この操作は安全ではありませんでした。i = 0の場合、v [0]は-1に設定されていませんが、奇妙な大きな値に設定されています(unsigned intを負にしようとしたときによく発生します)。

iの値をlongintにキャストする問題を解決できました

long int v[]={(long int) i - 1, (long int) i, (long int) i + 1};

そして、すべてが正常に機能しました(両方のマシンで)。

最初のマシンで正常に動作し、他のマシンでは動作しない理由がわかりません。

これを理解するのを手伝って、将来この問題や他の問題を回避できるようにしてください。

4

2 に答える 2

6

値の場合unsigned、加算/減算はモジュロ演算として明確に定義されているため0U-1、次のようになりstd::numeric_limits<unsigned>::max()ます。

符号なしから符号付きに変換するとき、変換先の型が符号なしの値のすべての値を保持するのに十分な大きさである場合、単純に変換先の型に直接データ コピーを行います。宛先タイプがすべての符号なし値を保持するのに十分な大きさでない場合、それは実装で定義されていると思います(標準参照を見つけようとします)。

したがって、long64ビットの場合(おそらく64ビットマシンの場合)、署名なしが適合し、そのままコピーされます。

longが 32 ビット マシンで 32 ビットの場合、ビット パターンを符号付きの値 (この場合は -1) として解釈する可能性が高くなります。

編集: これらの問題を回避する最も簡単な方法は、署名された型と署名されていない型を混在させないことです。概念上負の数が許可されていない値から 1 を引くとはどういう意味ですか? あなたの例では、関数パラメーターは符号付きの値でなければならないと主張します。

つまり、g++ (少なくともバージョン 4.5) は、-Wsign-conversion特定のコードでこの問題を検出する便利な機能を提供します。

于 2012-09-18T17:42:32.270 に答える
0

すべてのオーバーフロー キャストをキャッチする特殊なキャストを使用することもできます。

template<typename O, typename I>
O architecture_cast(I x) {
/* make sure I is an unsigned type. It  */
static_assert(std::is_unsigned<I>::value, "Input value to architecture_cast has to be unsigned");

assert(x <= static_cast<typename std::make_unsigned<O>::type>( std::numeric_limits<O>::max() ));

return static_cast<O>(x);
}

これを使用すると、結果の型が対応できるよりも大きな数からのすべてのキャストがデバッグでキャッチされます。これには、unsigned int が 0 で、-1 を引いた結果が最大の unsigned int になるケースが含まれます。

于 2016-04-08T16:31:35.433 に答える