签名/未签名比较

时间:2011-03-24 08:17:24

标签: c++ visual-studio-2005 comparison unsigned signed

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

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

我认为这与背景推广有关,但最后两个似乎不这么说。

在我看来,第一次==比较与其他人的签名/签名不匹配一样多吗?

5 个答案:

答案 0 :(得分:78)

比较signed with unsigned时,编译器会将signed值转换为unsigned。对于平等,这无关紧要,-1 == (unsigned) -1。对于其他比较,这很重要,例如以下是真实的:-1 > 2U

编辑:参考文献:

5/9 :(表达式)

  

许多期望的二元运算符   算术或枚举的操作数   类型导致转换和产量   结果类型以类似的方式。该   目的是产生一个共同的类型,   这也是结果的类型。   这种模式通常被称为   算术转换,是   定义如下:

     
      
  • 如果有的话   操作数是long double类型的   其他应转换为长   双。

  •   
  • 否则,如果是任一操作数   是两倍,另一个是   转换为双倍。

  •   
  • 否则,如果   操作数是浮点数,另一个是浮点数   应改为浮动。

  •   
  • 否则,积分促销   (4.5)应在两者上进行   operands.54)

  •   
  • 然后,如果是任一操作数   另一个应该是无符号的   转换为无符号长。

  •   
  • 否则,如果一个操作数是long   那么int和另一个unsigned int   如果一个long int可以代表所有的   unsigned int的值,   unsigned int应转换为a   long int;否则两个操作数   应转换为无符号长   中间体

  •   
  • 否则,如果任一操作数是   长,另一个应转换为   长。

  •   
  • 否则,如果是任一操作数   是未签名的,另一个是   转换为无符号。

  •   

4.7 / 2 :(整体转换)

  

如果目标类型未签名,   结果值最小   无符号整数与。一致   源整数(模2 n ,其中n是   用于表示的位数   无符号类型)。 [注意:在两个   补充表示,这   转换是概念性的,有   位模式没有变化(如果有的话)   不是截断)。 ]

EDIT2:MSVC警告级别

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

-1 == -1-1 == (unsigned) -1的含义相同 - 我发现这是一个直观的结果。

-1 < 2 -1 < (unsigned) 2的含义相同 - 乍一看这不太直观,IMO值得“早先”警告。

答案 1 :(得分:27)

为什么签名/未签名警告很重要,程序员必须注意它们,以下示例演示了这一点。

猜猜这段代码的输出?

#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 ,如果其类型已签名!

答案 2 :(得分:3)

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

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

4位示例:

1111 = 15?或-1?

所以如果你有1111&lt; 0001 ......这是不明确的......

但是如果你有1111 == 1111 ......虽然你不是故意的,但它是一样的。

答案 3 :(得分:1)

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

对我而言,奇怪的编译器不会在 a ==((int)b)上发出警告。我认为它应该给你一个整数截断警告或其他东西。

答案 4 :(得分:0)

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

Microsoft docs for C4389:

// 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或how to enable it in Visual Studio并没有太大帮助。

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

int a = 9;
unsigned int b = 10;
bool equal = (a == b); // C4388