コンパイラの警告 (レベル 2) C4146
符号付きの値を代入する変数は、符号付き型にキャストしなければなりません
符号なしの型では負でない値しか保持できないため、単項マイナス (否定) を符号なしの型に適用しても、通常は意味がありません。 オペランドと結果の両方が負ではありません。
解説
負の整数リテラルを表す場合、値の前の -
は単項マイナス演算子として解析されます。 コンパイラは、数値を解析した後にその演算子を適用します。 数値が符号なし整数型の範囲内に収まるが、対応する符号付き整数型の範囲内に収まらない場合、コンパイラはその値を符号なしとして解釈します。 符号なしの値は、単項マイナス演算子によって変更されません。
この警告は、最小の int
値 (-2147483648) や最小の long long
値 (-9223372036854775808) を表そうとする場合によく発生します。 これらの値は、どちらも -2147483648 または -9223372036854775808ll として記述することはできません。 その理由は、コンパイラが式を 2 つの段階で処理するためです。最初に数値を解析し、次に否定演算子を適用します。 たとえば、コンパイラが -2147483648 を解析する場合は、次の手順に従います。
数値 2147483648 が評価されます。 最大の
int
値 2147483647 より大きいが、unsigned int
内に収まるため、2147483648 の型はunsigned int
です。単項マイナスが符号なしの値に適用され、符号なしの結果が生成されます。これも 2147483648 になります。
結果の符号なし型は、予期しない動作を引き起こす可能性があります。 結果が比較で使用される場合、他のオペランドが int
であるときなどに符号なしの比較が使用される可能性があります。
C4146 を回避するには、<limits.h>
の INT_MIN
または LLONG_MIN
を使うか、C++ でそれに相当する <climits>
を使います。 これらの値は符号付きの型を持ちます。
/sdl
(追加のセキュリティ チェックを有効にする) コンパイラ オプションを使用すると、この警告がエラーに昇格されます。
例
次のサンプルは、コンパイラが警告 C4146 を生成する場合に発生する可能性がある予期しない動作を示しています。
// C4146.cpp
// compile with: /W2
#include <iostream>
void check(int i)
{
if (i > -9223372036854775808ll) // C4146
std::cout << i << " is greater than the most negative long long int.\n";
}
int main()
{
check(-100);
check(1);
}
この例では、コンパイラは -9223372036854775808ll を、リテラルに ll
サフィックスがあり、否定演算子が適用されているにもかかわらず、符号なしと見なします。 <
の比較を行うために、コンパイラは暗黙的に符号付き i
を unsigned long long int
に昇格します。 ((unsigned long long int)1) > 9223372036854775808ull
が false であるため、期待される 2 行目の 1 is greater than the most negative long long int
は出力されません。
この例を修正するには、<climits>
をインクルードし、-9223372036854775808ll を LLONG_MIN
に変更します。