c++ 为什么MSVC不对signed/unsigned ==比较发出警告?

c8ib6hqw  于 9个月前  发布在  其他
关注(0)|答案(6)|浏览(129)

我试图理解为什么下面的代码没有在指定的位置发出警告。

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

字符串
我以为这是为了背景宣传,但最后两个似乎不是这么说的。
在我看来,第一个==比较和其他比较一样是有符号/无符号的不匹配?

b4wnujal

b4wnujal1#

当比较signed和unsigned时,编译器将signed值转换为unsigned。对于相等性,这无关紧要,-1 == (unsigned) -1。对于其他比较,这很重要,例如,下面是true:-1 > 2U
编辑:参考文献:
5/9:(表达)
许多期望算术或枚举类型的操作数的二元运算符以类似的方式引起转换并产生结果类型。目的是产生一个公共类型,这也是结果的类型。这种模式称为通常的算术转换,其定义如下:

  • 如果其中一个操作数是long double类型,另一个操作数将被转换为long double。
  • 否则,如果一个操作数是double,另一个将被转换为double。
  • 否则,如果其中一个操作数是float,则另一个操作数应转换为float。
  • 否则,积分提升(4.5)应在两个操作数上执行。54)
  • 然后,如果一个操作数是无符号长,另一个将被转换为无符号长。
  • 否则,如果一个操作数是long int,另一个是unsigned int,那么如果long int可以表示unsigned int的所有值,则unsigned int应转换为long int;否则两个操作数都应转换为unsigned long int。
  • 否则,如果其中一个操作数为long,则另一个操作数应转换为long。
  • 否则,如果一个操作数是无符号的,另一个操作数将被转换为无符号。

4.7/2:(积分换算)
如果目标类型是无符号类型,则结果值是与源整数全等的最小无符号整数(模2n,其中n是用于表示无符号类型的位数)。[注意:在二进制补码表示中,这种转换是概念性的,位模式没有变化(如果没有截断)。]
编辑2:MSVC警告级别
在MSVC的不同警告级别上被警告的是,当然,开发人员做出的选择。在我看来,他们关于有符号/无符号相等与更大/更小比较的选择是有意义的,当然这完全是主观的:
-1 == -1的意思和-1 == (unsigned) -1一样-我发现这是一个直观的结果。
-1 < 2 * 并不 * 意味着与-1 < (unsigned) 2相同-乍一看,这不太直观,IMO值得“更早”警告。

hec6srdp

hec6srdp2#

为什么有符号/无符号警告很重要,程序员必须注意它们,下面的例子演示了这一点。
猜猜这段代码的输出是什么?

#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如果其类型是有符号的!

v64noz0r

v64noz0r3#

从C++20开始,我们有特殊的函数来正确比较有符号和无符号的值https://en.cppreference.com/w/cpp/utility/intcmp

s5a0g9ez

s5a0g9ez4#

==运算符只是进行按位比较(通过简单的除法来判断是否为0)。
小于/大于比较更多地依赖于数字的符号。
4位示例:
1111 = 15?或-1?
所以如果你有1111 < 0001..
但是如果你有1111 == 1111...这是同样的事情,虽然你不是故意的。

wmomyfyw

wmomyfyw5#

在一个使用2-补码表示值的系统中(大多数现代处理器),它们即使在二进制形式下也是相等的。这可能是为什么编译器不会抱怨a == b的原因。
对我来说,奇怪的是编译器没有警告你a ==((int)B)。我认为它应该给予你一个整数截断警告或其他东西。

2o7dmzc5

2o7dmzc56#

有问题的代码行不会生成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

相关问题