//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;
我认为这与后台促销有关,但是最后两个似乎不然。
在我看来,第一个
==
比较是否和其他符号一样,是有符号/无符号不匹配?#1 楼
当比较带符号和无符号时,编译器会将带符号的值转换为无符号。对于平等,这无关紧要-1 == (unsigned) -1
。对于其他比较而言,例如满足以下条件:-1 > 2U
。编辑:引用:
5/9 :(表达式)
许多二进制运算符Expect
算术或枚举类型的操作数会以类似方式导致转换和yield
结果类型。
的目的是产生一个通用类型,
也是结果的类型。
这种模式称为通常的
算术转换,它是
定义的如下所示:
如果任一
操作数的类型为long double,则
other将转换为long
double。
否则,如果一个操作数
是双精度数,则另一个
应转换为双精度数。
否则,如果
其中一个操作数为浮点数,则另一个
应转换为浮点数。
否则,必须对两个操作数都执行整数提升
(4.5)
操作数。54)
然后,如果任何一个操作数
是无符号的,则另一个
应转换为无符号的long。
否则,如果一个操作数是long
int而另一个是无符号int,则
如果long int可以表示无符号int的所有
值,则
unsigned int应转换为
long int;否则,两个操作数
都应转换为无符号long
int。
否则,如果一个操作数为
long,则另一个操作数应转换为
long。
否则,如果任何一个操作数
是无符号的,则另一个应被
转换为无符号。
4.7 / 2 :(整数转换)
如果目标类型是无符号的,则
结果值是与
源整数一致的最小的
无符号整数(模2n,其中n是
用于表示的位数
无符号类型)。 [注意:在二进制的
补码表示中,此
转换是概念性的,并且
位模式没有变化(如果没有截断的话)。 ]
EDIT2:MSVC警告级别
当然,关于MSVC不同警告级别的警告是开发人员的选择。正如我所看到的,他们关于有符号/无符号相等与更大/更少比较的选择是有意义的,这当然是完全主观的:
-1 == -1
的含义与-1 == (unsigned) -1
相同-我发现直观的结果。-1 < 2
的含义与-1 < (unsigned) 2
的含义不同-乍看之下不太直观,IMO应该得到“较早的”警告。评论
如何将有符号转换为无符号?什么是带符号的值-1的无符号版本? (有符号-1 = 1111,而无符号15 = 1111,按位它们可能相等,但它们在逻辑上不相等。)我知道如果强制执行此转换将起作用,但是编译器为什么会这样做呢?这是不合逻辑的。此外,正如我在上面评论的那样,当我打开警告时,出现了丢失的==警告,这似乎可以支持我所说的内容?
–彼得
2011年3月24日上午9:00
如4.7 / 2所述,有符号为无符号表示二进制补码的位模式没有变化。至于为什么编译器这样做,则C ++标准要求。我相信VS警告在不同级别背后的原因是表达无意的可能性-我同意他们的观点,即有符号/无符号的相等比较比不相等比较“不太可能”成为问题。当然这是主观的-这些是VC编译器开发人员做出的选择。
– Erik
2011-3-24在9:03
好吧,我想我差不多了。我读的是编译器在概念上做的事情:'if((((unsigned _int64)0x7fffffff)==(((unsigned _int64)0xffffffff))'',因为_int64是可同时表示0x7fffffff和0xffffffff的最小类型用未签名的术语?
–彼得
2011-3-24在9:34
实际上,与(无符号)-1或-1u进行比较通常比与-1进行比较差。这是因为(unsigned __int64)-1 == -1,但是(unsigned __int64)-1!=(unsigned)-1。因此,如果编译器发出警告,则尝试通过强制转换为无符号或使用-1u使其静音,如果该值实际上恰好是64位,或者恰好在以后将其更改为1,则将破坏代码!请记住,size_t是无符号的,仅在64位平台上是64位的,并且使用-1表示无效值非常常见。
– Jan Hudec
2011-11-25 8:14
也许cpmpilers不应该那么做。如果比较带符号和无符号,只需检查带符号的值是否为负。如果是这样,则无论如何都保证小于未签名的那个。
– CashCow
2014年12月17日下午16:28
#2 楼
下例演示了为什么签名/未签名警告很重要,并且程序员必须注意它们。猜猜这段代码的输出吗?
#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(i <((int)j)”,则它会按预期工作。尽管“ if(i <((__int64)j))更有意义(假设您不能这样做,则_int64的大小是int的两倍)。
–彼得
2011-3-24在9:08
@Peter“为什么不将unsgiend转换为(较大的)有符号值?”答案很简单:可能没有更大的符号值。在32位机器上,在long long之前的日子里,int和long都是32位,并且没有什么比这更大的了。比较有符号和无符号时,最早的C ++编译器都将它们都转换为有符号。我忘记了什么原因,C标准委员会对此进行了更改。最好的解决方案是尽可能避免未签名。
–詹姆斯·坎泽(James Kanze)
2011-3-24在10:58
@JamesKanze:我怀疑它也必须与事实有关,即有符号溢出的结果是未定义行为,而没有无符号溢出的结果不是,因此定义了将负无符号值转换为无符号,而将大无符号值转换为负符号。价值不是。
– Jan Hudec
2011-11-25 8:26
@James编译器始终可以生成可实现此比较的更直观语义的程序集,而无需转换为更大的类型。在此特定示例中,首先检查i <0是否足够。那么我肯定比j小。如果i不小于零,则可以安全地将ì转换为无符号以将其与j进行比较。当然,有符号和无符号之间的比较会比较慢,但是从某种意义上来说,它们的结果会更正确。
–Sven
2014年9月2日在12:52
@Sven我同意。该标准可能要求比较才能适用于所有实际值,而不是转换为两种类型之一。但是,这仅适用于比较;我怀疑委员会不希望对比较和其他操作使用不同的规则(也不想攻击实际上不存在要比较的类型时指定比较的问题)。
–詹姆斯·坎泽(James Kanze)
2014年9月2日在15:17
#3 楼
==运算符只是进行按位比较(通过简单除法以查看它是否为0)。比比较大/小比对数字的依赖更多。
4位示例:
1111 = 15?或-1?
所以如果你有1111 <0001 ...就是模棱两可的...
但是如果你有1111 == 1111 ...是同一件事虽然你不是故意的。
评论
我理解这一点,但无法回答我的问题。如您所指出的,如果符号不匹配,则1111!= 1111。编译器知道这些类型不匹配,那么为什么不警告呢? (我的观点是我的代码可能包含许多这样的不匹配,因此我没有受到警告。)
–彼得
2011-3-24在8:38
这就是它的设计方式。相等性测试检查相似性。和它相似。我同意你的看法,不应该这样。您可以执行宏或将x == y重载为!((x
– Yochai Timmer
2011-3-24在8:44
#4 楼
在使用2补码(大多数现代处理器)表示值的系统中,即使它们的二进制形式也相等。这可能就是为什么编译器不抱怨== b的原因。对我来说,奇怪的是编译器没有对==((int)b)发出警告。我认为它应该为您提供整数截断警告或其他内容。
评论
C / C ++的理念是:编译器相信开发人员知道在类型之间进行显式转换时他正在做什么。因此,没有警告(至少默认情况下-如果警告级别设置为高于默认值,我相信会有编译器为此生成警告)。
–彼得·托克(PéterTörök)
2011-3-24在8:51
#5 楼
有问题的代码行不会生成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
评论
当使用'-Wall'调用时,gcc 4.4.2打印警告这是推测,但也许可以优化所有比较,因为它在编译时知道答案。
啊!回覆。 bobah的评论:我打开了所有警告,现在出现缺少的警告。我认为它应该与其他比较出现在相同的警告级别设置。
@bobah:我真的很讨厌gcc 4.4.2会打印警告(没有办法告诉它只为不平等而打印),因为所有消除警告的方式都会使情况变得更糟。默认提升可将-1或〜0可靠地转换为任何无符号类型的最高可能值,但是如果您自己强制转换警告以使其静音,则必须知道确切的类型。因此,如果您更改类型(将其扩展为unsigned long long),则与裸露-1的比较仍然可以工作(但那些警告),而与-1u或(unsigned)-1的比较都将不幸失败。 />
我不知道为什么需要警告,以及为什么编译器无法使其工作。 -1为负数,因此小于任何无符号数字。简单。