有符号和无符号整数表达式与0x80000000之间的比较

时间:2015-06-27 12:46:51

标签: c++

我有以下代码:

#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是无符号整数?我可以以某种方式签名以摆脱警告吗?

据我所知,0x80000000将是INT_MIN,因为它超出正整数的范围。但为什么编译器会假设我想要一个正数?

我正在linux上使用gcc版本4.8.1 20130909进行编译。

3 个答案:

答案 0 :(得分:5)

0x80000000是一个unsigned int,因为该值太大而无法放入int,并且您没有添加任何L来指定它是一个很长的。

发出警告是因为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]);
}

这段代码似乎很好,但有一个错误。如果pts向量为空,pts.size()0但是,这里出现了令人惊讶的部分,pts.size()-1是一个巨大的无意义数字(今天通常为4294967295,但取决于平台)和循环将使用无效索引(具有未定义的行为)。

此处将变量更改为size_t i会删除警告但不会有帮助,因为同样的错误仍然存​​在...

问题的核心是,无符号值a < b-1a+1 < b即使对于非常常使用的值(如零)也不一样;这就是为什么在容器大小等非负值上使用无符号类型是个坏主意和错误来源的原因。

另请注意,在该值不适合整数的平台上,您的代码不是正确的可移植C ++,因为为unsigned类型定义了溢出行为,而不是常规整数。依赖于整数超过限制时发生的事情的C ++代码具有未定义的行为。

即使您知道特定硬件平台上发生了什么,也请注意允许编译器/优化器假设有符号整数溢出永远不会发生:例如像a < a+1这样的测试a是常规的int始终为真。

答案 1 :(得分:2)

您似乎混淆了两个不同的问题:编码某事意义。这是一个例子:你看到一个数字97.这是一个十进制编码。但这个数字的含义完全不同。它可以表示ASCII&#39; a&#39;字符,非常热的温度,三角形中的几何角等。您无法从编码中推断出含义。有人必须为您提供上下文(如ASCII地图,温度等)。

回到你的问题:0x80000000正在编码。虽然INT_MIN是有意义的。没有可互换的,没有可比性。在某些情况下,在特定的硬件上,它们可能是相同的,就像97和&#39; a&#39;在ASCII上下文中是相等的。

编译器会警告您意义不明确,而不是编码。赋予特定编码意义的一种方法是转换操作符。与(unsigned short)-17(student*)ptr;

相同

在具有后兼容性intunsigned int的32位系统或64位上,编码为32位,如0x80000000,但64位MIN_INT不等于此号。

无论如何 - 您的问题的答案:为了删除警告,您必须为比较的左右表达提供相同的上下文。 你可以通过很多方式做到这一点。例如:

只要您遵守以下规则,

(unsigned int)a == (unsigned int)0x80000000(__int64)a == (__int64)0x80000000甚至是疯狂的(char *)a == (char *)0x80000000或任何其他方式:

  1. 您不能降级编码(不要减少它所需的位数)。像(char)a == (char)0x80000000一样不正确,因为你将32位降级为8位
  2. 您必须为==运算符的左侧和右侧赋予相同的上下文。与(char *)a == (unsigned short)0x80000000不一样,会产生错误/警告。
  3. 我想再举一个例子,说明编码和意义之间的区别是多么重要。看一下代码

    char a = -7;  
    bool b = (a==-7) ? true : false;
    

    'b'的结果是什么?答案会震惊你:它是未定义的。 一些编译器(通常是Microsoft visual studio)将编译一个程序,b将获得true,而在Android NDK编译器上b将获得false。 原因是Android NDK会对待char&#39;输入&#39; unsigned char&#39;,而Visual Studio会对待&#39; char&#39;作为&#39; signed char&#39;。所以在Android手机上,-7的编码实际上具有249的含义,并且不等于(int)-7的含义。 解决此问题的正确方法是明确定义“问题”。签名char:

     signed char a = -7;  
     bool b = (a==-7) ? true : false;
    

答案 2 :(得分:1)

默认情况下,0x80000000被视为无符号。 你可以避免这样的警告:

    if (a == (int)0x80000000)
        a=42;

评论后编辑:

另一种(也许是更好的)方式是

    if ((unsigned)a == 0x80000000)
        a=42;