签名/未签名比较

Pet*_*ter 77 c++ comparison unsigned signed visual-studio-2005

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

//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;
Run Code Online (Sandbox Code Playgroud)

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

在我看来,第一次==比较与其他比较同样是签名/未签名的不匹配?

Eri*_*rik 83

将signed with signed与unsigned进行比较时,编译器会将signed值转换为unsigned.为了平等,这无所谓-1 == (unsigned) -1.对于其他比较,这很重要,例如以下情况:-1 > 2U.

编辑:参考文献:

5/9 :(表达式)

许多期望算术或枚举类型的操作数的二元运算符会以类似的方式引起转换并产生结果类型.目的是产生一个通用类型,它也是结果的类型.此模式称为通常的算术转换,其定义如下:

  • 如果任一操作数的类型为long double,则另一个操作数应转换为long double.

  • 否则,如果任一操作数为double,则另一个操作数应转换为double.

  • 否则,如果任一操作数是浮点数,则另一个操作数应转换为浮点数.

  • 否则,应在两个操作数上执行整数促销(4.5).54)

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

  • 否则,如果一个操作数是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应该得到"更早"的警告.

  • 如何将有符号转换为无符号?有符号值 -1 的无符号版本是什么?(有符号 -1 = 1111,而无符号 15 = 1111,按位它们可能相等,但它们在逻辑上不相等。)我明白如果你强制这种转换它会起作用,但为什么编译器会这样做?这不合逻辑。此外,正如我上面评论的那样,当我打开警告时,缺少 == 警告出现了,这似乎支持我说的话? (3认同)
  • 实际上,与`(无符号)-1'或'-1u'相比,与'-1'相比,往往更差**.那是因为`(unsigned __int64)-1 == -1`,但是`(unsigned __int64)-1!=(unsigned)-1`.因此,如果编译器发出警告,你会尝试通过强制转换为无符号或使用`-1u`来使其静默,如果该值实际上恰好是64位,或者您碰巧将其更改为一个,那么您将破坏您的代码!请记住,`size_t`是无符号的,64位平台上只有64位,而使用-1表示无效值非常常见. (2认同)

Naw*_*waz 29

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

猜猜这段代码的输出?

#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;
}
Run Code Online (Sandbox Code Playgroud)

输出:

i is greater than j
Run Code Online (Sandbox Code Playgroud)

惊讶吗?在线演示:http://www.ideone.com/5iCxY

底线:相比之下,如果一个操作数是unsigned,则另一个操作数unsigned 其类型被签名时被隐式转换为!

  • @Peter"为什么不将unsgiend转换为(更大的)签名值?" 答案很简单:可能没有更大的签名值.在32位机器上,在很长一段时间之后,int和long都是32位,并没有更大的东西.在比较signed和unsigned时,最早的C++编译器将两者都转换为signed.因为我忘记了什么原因,C标准委员会改变了这一点.您最好的解决方案是尽可能避免无符号. (5认同)
  • @JamesKanze:我怀疑它还必须做一些事实,签名溢出的结果是未定义的行为,而无符号溢出的结果不是,因此在将大的无符号值转换为负的时,定义了负有符号值到无符号的转换值是*不*. (5认同)
  • 他是对的!这是愚蠢的,但他是对的.这是我以前从未遇到的主要问题.为什么不将unsigned转换为(更大的)签名值?!如果你做"if(i <((int)j))"它就像你期望的那样工作.虽然"if(i <((_int64)j))"会更有意义(假设,你不能,_int64是int的两倍). (2认同)
  • @James编译器总是可以生成程序集,该程序集将实现此比较的更直观的语义,而不会转换为更大的类型.在这个特定的例子中,首先检查是否"i <0"就足够了.然后`i`肯定比`j`小.如果`i`不小于零,那么```可以安全地转换为无符号,以便与`j`进行比较.当然,有符号和无符号之间的比较会更慢,但在某种意义上它们的结果会更正确. (2认同)

hdn*_*dnn 6

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


Yoc*_*mer 5

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

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

4 位示例:

1111 = 15?或 -1 ?

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

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