为什么 MSVC 不对有符号/无符号 == 比较发出警告?

问题描述 投票:0回答:6

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

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

我以为是后台推广有关,但最后两个好像不是这么说的。

在我看来,第一个

==
比较与其他比较一样是有符号/无符号不匹配?

c++ visual-studio-2005 comparison unsigned signed
6个回答
120
投票

比较有符号和无符号时,编译器会将有符号值转换为无符号值。为了平等,这并不重要,

-1 == (unsigned) -1
。对于其他比较很重要,例如以下说法正确的是:
-1 > 2U
.

编辑:参考文献:

5/9:(表达式)

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

  • 如果其中之一 操作数的类型为 long double, 其他应转换为长 双倍。
  • 否则,如果任一操作数 是双倍,另一个是 转换为双倍。
  • 否则,如果 一个操作数是 float,另一个是 float 应转换为浮点数。
  • 否则积分优惠 (4.5) 应同时执行 操作数.54)
  • 那么,如果任一操作数 是无符号长,另一个应是 转换为无符号长整型。
  • 否则,如果一个操作数是 long int 和另一个无符号 int,然后 如果一个long int可以代表所有 unsigned int 的值, unsigned int 应转换为 长整型;否则两个操作数 应转换为 unsigned long int.
  • 否则,如果任一操作数是 long,另一个应转换为 长。
  • 否则,如果任一操作数 未签名的,其他应为 转换为无符号。

4.7/2:(积分转换)

如果目标类型是无符号的, 结果值是最小的 无符号整数等同于 源整数(模 2n,其中 n 是 用于表示的位数 无符号类型)。 [注:在两个 补表示法,这 转换是概念性的,并且有 位模式没有变化(如果有 没有截断)。 ]

EDIT2:MSVC 警告级别

MSVC不同警告级别所警告的内容当然是开发者自己的选择。在我看来,他们在有符号/无符号相等与更大/更少比较方面的选择是有意义的,当然这完全是主观的:

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

-1 < 2
-1 < (unsigned) 2
意思相同 - 乍一看不太直观,IMO 值得“更早”警告。


39
投票

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

猜猜这段代码的输出?

#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
if其类型有符号!


6
投票

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


5
投票

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

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

4 位示例:

1111 = 15 ?或-1?

如果您有 1111 < 0001 ... it's ambiguous...

但是如果你有 1111 == 1111 ...这是同样的事情,尽管你不是故意的。


2
投票

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

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


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

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

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

int a = 9;
unsigned int b = 10;
bool equal = (a == b); // C4388
© www.soinside.com 2019 - 2024. All rights reserved.