94

我试图了解为什么以下代码不会在指定位置发出警告。

//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;

我以为是和背景推广有关,但最后两个似乎不是这么说的。

在我看来,第一个==比较与其他比较一样多是有符号/无符号不匹配?

4

5 回答 5

107

在比较有符号和无符号时,编译器会将有符号值转换为无符号值。对于平等,这无关紧要,-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:(积分转换)

如果目标类型是无符号的,则结果值是与源整数一致的最小无符号整数(模 2 n,其中 n 是用于表示无符号类型的位数)。[注意:在二进制补码表示中,这种转换是概念性的,位模式没有变化(如果没有截断)。]

EDIT2:MSVC 警告级别

MSVC 的不同警告级别所警告的内容当然是开发人员做出的选择。正如我所看到的,他们在有符号/无符号相等与更大/更少比较方面的选择是有道理的,这当然是完全主观的:

-1 == -1意思是一样的-1 == (unsigned) -1——我发现这是一个直观的结果。

-1 < 2 并不意味着相同-1 < (unsigned) 2- 乍一看,这不太直观,IMO 应该得到“更早”的警告。

于 2011-03-24T08:27:45.813 回答
36

为什么有符号/无符号警告很重要,程序员必须注意它们,下面的例子说明了这一点。

猜猜这段代码的输出?

#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

于 2011-03-24T08:49:52.387 回答
5

== 运算符只是进行按位比较(通过简单的除法来查看它是否为 0)。

比比较更小/更大更依赖于数字的符号。

4 位示例:

1111 = 15 ? 或 -1 ?

所以如果你有 1111 < 0001 ...这是模棱两可的...

但是如果你有 1111 == 1111 ......虽然你不是故意的,但这是同一件事。

于 2011-03-24T08:28:12.023 回答
2

在使用 2-补码(大多数现代处理器)表示值的系统中,即使是二进制形式,它们也是相等的。这可能就是编译器不抱怨a == b 的原因。

对我来说,奇怪的是编译器没有在a == ((int)b)上警告你。我认为它应该给你一个整数截断警告或其他东西。

于 2011-03-24T08:29:50.227 回答
1

有问题的代码行不会生成 C4018 警告,因为 Microsoft 已使用不同的警告编号(即C4389)来处理这种情况,并且默认情况下未启用 C4389(即在级别 3)。

来自C4389的Microsoft 文档:

// 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;
};

其他答案很好地解释了为什么 Microsoft 可能决定从相等运算符中制作一个特殊情况,但我发现如果不提及 C4389 或如何在 Visual Studio 中启用它,这些答案并不是很有帮助。

我还应该提到,如果您要启用 C4389,您还可以考虑启用 C4388。不幸的是,没有 C4388 的官方文档,但它似乎以如下表达式弹出:

int a = 9;
unsigned int b = 10;
bool equal = (a == b); // C4388
于 2020-05-19T07:33:42.367 回答