我试图了解为什么以下代码不会在指定位置发出警告。
//from limits.h
#define UINT_MAX 0xffffffff /* maximum unsigned int value */
#define INT_MAX 2147483647 /* maximum (signed) int value */
/* = 0x7fffffff */
int a = INT_MAX;
//_int64 a = INT_MAX; // makes all warnings go away
unsigned int b = UINT_MAX;
bool c = false;
if(a < b) // warning C4018: '<' : signed/unsigned mismatch
c = true;
if(a > b) // warning C4018: '<' : signed/unsigned mismatch
c = true;
if(a <= b) // warning C4018: '<' : signed/unsigned mismatch
c = true;
if(a >= b) // warning C4018: '<' : signed/unsigned mismatch
c = true;
if(a == b) // no warning <--- warning expected here
c = true;
if(((unsigned int)a) == b) // no warning (as expected)
c = true;
if(a == ((int)b)) // no warning (as expected)
c = true;
我以为是和背景推广有关,但最后两个似乎不是这么说的。
在我看来,第一个 ==
比较与其他比较一样多是有符号/无符号不匹配?
-1
的比较仍然有效(但会发出警告),而您与 -1u
或 (unsigned)-1
的比较都将失败。
在比较有符号和无符号时,编译器会将有符号值转换为无符号值。对于平等,这无关紧要,-1 == (unsigned) -1
。对于其他比较而言,它很重要,例如以下是正确的:-1 > 2U
。
编辑:参考:
5/9:(表达式)
许多期望算术或枚举类型的操作数的二元运算符会以类似的方式导致转换和产生结果类型。目的是产生一个通用类型,这也是结果的类型。这种模式称为通常的算术转换,其定义如下:
如果任一操作数是 long double 类型,则另一个应转换为 long double。
否则,如果任一操作数为双精度,则另一个应转换为双精度。
否则,如果任一操作数为浮点数,则另一个应转换为浮点数。
否则,应在两个操作数上执行积分提升 (4.5)。54)
然后,如果其中一个操作数是 unsigned long,则另一个操作数应转换为 unsigned long。
否则,如果一个操作数是 long int 而另一个是 unsigned int,则如果 long int 可以表示 unsigned int 的所有值,则 unsigned int 应转换为 long int;否则,两个操作数都应转换为 unsigned long int。
否则,如果任一操作数为 long,则另一个应转换为 long。
否则,如果任一操作数是无符号的,则另一个应转换为无符号。
4.7/2:(积分转换)
如果目标类型是无符号的,则结果值是与源整数一致的最小无符号整数(模 2n,其中 n 是用于表示无符号类型的位数)。 [注意:在二进制补码表示中,这种转换是概念性的,位模式没有变化(如果没有截断)。 ]
EDIT2:MSVC 警告级别
MSVC 的不同警告级别所警告的内容当然是开发人员做出的选择。正如我所看到的,他们关于有符号/无符号相等与更大/更少比较的选择是有道理的,这当然是完全主观的:
-1 == -1
与 -1 == (unsigned) -1
的含义相同 - 我发现这是一个直观的结果。
-1 < 2
不与 -1 < (unsigned) 2
的意思相同 - 乍一看不太直观,IMO 应该“提前”警告。
为什么有符号/无符号警告很重要,程序员必须注意它们,下面的例子说明了这一点。
猜猜这段代码的输出?
#include <iostream>
int main() {
int i = -1;
unsigned int j = 1;
if ( i < j )
std::cout << " i is less than j";
else
std::cout << " i is greater than j";
return 0;
}
输出:
i is greater than j
惊讶吗?在线演示:http://www.ideone.com/5iCxY
底线:相比之下,如果一个操作数是 unsigned
,那么另一个操作数会隐式转换为 unsigned
如果它的类型是有符号的!
i<0
就足够了。那么 i
肯定小于 j
。如果 i
不小于零,则 ì
可以安全地转换为无符号以与 j
进行比较。当然,有符号和无符号之间的比较会更慢,但它们的结果在某种意义上会更正确。
== 运算符只是进行按位比较(通过简单的除法来查看它是否为 0)。
比比较更小/更大更依赖于数字的符号。
4 位示例:
1111 = 15 ?或 -1 ?
所以如果你有 1111 < 0001 ...这是模棱两可的...
但是如果你有 1111 == 1111 ......虽然你不是故意的,但这是同一件事。
在使用 2-补码(大多数现代处理器)表示值的系统中,即使是二进制形式,它们也是相等的。这可能就是编译器不抱怨 a == b 的原因。
对我来说,奇怪的是编译器不会在 == ((int)b) 上警告你。我认为它应该给你一个整数截断警告或其他东西。
有问题的代码行不会生成 C4018 警告,因为 Microsoft 已使用不同的警告编号(即 C4389)来处理这种情况,并且默认情况下未启用 C4389(即在级别 3)。
从 C4389 的 Microsoft docs:
// C4389.cpp
// compile with: /W4
#pragma warning(default: 4389)
int main()
{
int a = 9;
unsigned int b = 10;
if (a == b) // C4389
return 0;
else
return 0;
};
其他答案很好地解释了为什么微软可能决定用相等运算符制作一个特殊情况,但我发现如果不提及 C4389 或 how to enable it in Visual Studio,这些答案并不是很有帮助。
我还应该提到,如果您要启用 C4389,您还可以考虑启用 C4388。不幸的是,没有 C4388 的官方文档,但它似乎以如下表达式弹出:
int a = 9;
unsigned int b = 10;
bool equal = (a == b); // C4388
(unsigned)-1
或-1u
进行比较通常比与-1
进行比较差。那是因为(unsigned __int64)-1 == -1
,但是(unsigned __int64)-1 != (unsigned)-1
。因此,如果编译器发出警告,您尝试通过强制转换为无符号或使用-1u
来使其静音,并且如果该值实际上是 64 位,或者您后来碰巧将其更改为 64 位,那么您将破坏您的代码!请记住,size_t
是无符号的,仅在 64 位平台上为 64 位,并且使用 -1 表示无效值非常常见。