[英]comparison between signed and unsigned integer expressions and 0x80000000
我有以下代码:
#include <iostream>
using namespace std;
int main()
{
int a = 0x80000000;
if(a == 0x80000000)
a = 42;
cout << "Hello World! :: " << a << endl;
return 0;
}
输出是
Hello World! :: 42
所以比较有效。 但是编译器告诉我
g++ -c -pipe -g -Wall -W -fPIE -I../untitled -I. -I../bin/Qt/5.4/gcc_64/mkspecs/linux-g++ -o main.o ../untitled/main.cpp
../untitled/main.cpp: In function 'int main()':
../untitled/main.cpp:8:13: warning: comparison between signed and unsigned integer expressions [-Wsign-compare]
if(a == 0x80000000)
^
所以问题是:为什么0x80000000是unsigned int? 我可以以某种方式签名以摆脱警告吗?
据我所知,0x80000000将是INT_MIN,因为它超出正整数的范围。 但为什么编译器会假设我想要一个正数?
我正在使用linux上的gcc版本4.8.1 20130909进行编译。
0x80000000
是一个unsigned int,因为该值太大而不适合int
并且您没有添加任何L
来指定它是一个long。
发出警告是因为C / C ++中的unsigned
具有非常奇怪的语义,因此通过混合有符号和无符号整数很容易在代码中出错。 这种混合通常是错误的来源,特别是因为历史事故标准库选择使用无符号值作为容器的大小( size_t
)。
我经常用一个例子来说明问题的微妙程度
// Draw connecting lines between the dots
for (int i=0; i<pts.size()-1; i++) {
draw_line(pts[i], pts[i+1]);
}
这段代码似乎很好,但有一个bug。 如果pts
向量为空,则pts.size()
为0
但是,这里出现了令人惊讶的部分, pts.size()-1
是一个巨大的无意义数字(今天经常是4294967295,但取决于平台)并且循环将使用无效索引(具有未定义的行为)。
在这里将变量更改为size_t i
将删除警告但不会有帮助,因为同样的bug仍然存在...
问题的核心是,对于无符号值,即使对于非常常用的值(如零), a < b-1
和a+1 < b
也不是一回事; 这就是为什么对容器大小这样的非负值使用无符号类型是一个坏主意和bug的来源。
另请注意,在该值不适合整数的平台上,您的代码不是正确的可移植C ++,因为溢出的行为是为unsigned
类型定义的,而不是针对常规整数定义的。 依赖于整数超过限制时发生的事情的C ++代码具有未定义的行为。
即使你知道特定硬件平台上发生了什么,也要注意允许编译器/优化器假设有符号整数溢出永远不会发生:例如像a < a+1
这样a < a+1
测试,其中a
是常规int
可以认为总是为真一个C ++编译器。
看起来你混淆了两个不同的问题: 某事物的编码和某事物的意义 。 这是一个例子:你看到一个数字97.这是一个十进制编码。 但这个数字的含义完全不同。 它可以表示ASCII'a'字符,非常热的温度,三角形中的几何角等。您无法从编码中推断出含义。 有人必须为您提供上下文(如ASCII地图,温度等)。
回到你的问题: 0x80000000
是编码。 虽然INT_MIN
是有意义的。 没有可互换的,没有可比性。 在某些上下文中的特定硬件上,它们可能相等,就像97和'a'在ASCII上下文中相同。
编译器会警告你有意义的歧义,而不是编码。 赋予特定编码意义的一种方法是转换操作符。 喜欢(unsigned short)-17
或(student*)ptr;
在具有后兼容性int
和unsigned int
的32位系统或64位上具有32位的编码,如在0x80000000
但在64位上, MIN_INT
将不等于该数字。
无论如何 - 您的问题的答案:为了删除警告,您必须为比较的左右表达式提供相同的上下文。 你可以通过很多方式做到这一点。 例如:
(unsigned int)a == (unsigned int)0x80000000
或(__int64)a == (__int64)0x80000000
甚至是疯狂(char *)a == (char *)0x80000000
或任何其他方式,只要你保持以下规则:
(char)a == (char)0x80000000
是不正确的,因为你将32位降级为8位 (char *)a == (unsigned short)0x80000000
不正确会产生错误/警告。 我想再举一个例子,说明编码和意义之间的区别是多么重要。 看看代码
char a = -7;
bool b = (a==-7) ? true : false;
'b'
的结果是什么? 答案会震惊你:它是未定义的。 一些编译器(通常是Microsoft visual studio)将编译一个程序,在Android NDK编译器上b将变为true
,b将变为false
。 原因是Android NDK将' char
'类型视为' unsigned char
',而Visual studio将' char
'视为' signed char
'。 所以在Android手机上,-7的编码实际上具有249的含义,并且不等于(int)-7的含义。 解决此问题的正确方法是将'a'专门定义为signed char:
signed char a = -7;
bool b = (a==-7) ? true : false;
默认情况下,0x80000000被视为无符号。 你可以避免这样的警告:
if (a == (int)0x80000000)
a=42;
评论后编辑:
另一种(也许是更好的)方式
if ((unsigned)a == 0x80000000)
a=42;
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.