为什么负整数比无符号整数大?

43
int main(void) 
{ 
     unsigned int y = 10; 
     int x = – 4; 
     if (x > y) 
        Printf("x is greater");
     else 
        Printf("y is greater"); 
     getch(); 
     return (0); 
} 

Output: x is greater

我原以为输出结果会是y更大,因为它是无符号的,那么这背后的原因是什么呢?


7
这句话的翻译是:这应该会给你一个警告吧? - asheeshr
6个回答

47
由于 int 值在 32 位计算机上被提升为 unsigned int,特别是 0xFFFFFFFC,作为 unsigned int 的值为 4294967292,远大于 10
根据 C99 6.3.1.1-p2,如果 int 可以表示原始类型的所有值(对于位域来说,由宽度限制),则该值转换为 int;否则,将其转换为 unsigned int。这些称为整数提升。其他所有类型都不受整数提升的影响。
执行转换的方法是:根据 C99 6.3.1.3-p2,如果新类型是无符号的,则通过重复加上或减去可以在新类型中表示的最大值加一,直到该值在新类型范围内为止。
这基本上意味着“添加 UINT_MAX+1”(至少我是这么理解的)。
关于为什么提升到 unsigned int 侧的问题; 按照优先级:
根据 C99 6.3.1.8-p1,如果具有无符号整数类型的操作数的等级大于或等于其他操作数的类型的等级,则带有有符号整数类型的操作数将转换为具有无符号整数类型的操作数的类型。
否则,如果带有有符号整数类型的操作数的类型可以表示无符号整数类型的所有值,则将带有无符号整数类型的操作数转换为带有有符号整数类型的操作数的类型。
这告诉我 int vs. unsigned char 应该按预期工作。 测试
int main()
{
    int x = -4;
    unsigned int y = 10;
    unsigned char z = 10;

    if (x > y)
        printf("x>y\n");
    else
        printf("x<y\n");

    if (x > z)
        printf("x>z\n");
    else
        printf("x<z\n");
    return 0;
}

输出

x>y
x<z

看这个。


1
我想知道为什么 unsigned int 类型的变量 y 没有被转换成 signed int,因为它在 unsigned int 的范围内。 - Anirudha
6
相关的部分实际上是 6.3.1.8 常规算术转换。它说:“...否则,如果具有无符号整数类型的操作数的等级大于或等于另一个操作数的类型的等级,则带有有符号整数类型的操作数将被转换为带有无符号整数类型的操作数的类型。”(intunsigned int 的等级相等)。 - melpomene
当然可以,继续添加吧。 - melpomene

12

在比较有符号值和无符号值时,会在 "无符号空间" 中进行比较。也就是说,有符号值将通过添加 UINT_MAX + 1 转换为无符号值。在使用二进制补码表示负数值的实现中,不需要对这些值进行特殊处理。

在此示例中,-4 被转换为 0x100000000-4 = 0xFFFFFFFC,明显大于 > 10


2
在回答问题时被踩却不知道踩你的人认为哪里有问题,这是一件很棒的事情。 - glglgl
该行为不依赖于实现,并且与二进制补码无关。 - melpomene
1
不,不是这样的。答案是正确的,就像页面上的所有答案一样。对于所有有经验的C程序员来说,这都是微不足道的小事。 - Israel Unterman
1
那又怎样,它不需要也没关系。实现使用2的补码。你可以强制转换为无符号并检查值。 - Israel Unterman
@WhozCraig (-4 + (0xFFFFFFFF+1))0x100000000-4 有什么区别?我并没有声称提到了2s补码,但使用它的系统“没有任何关系”,这是粗略地表达。 - glglgl
显示剩余2条评论

4
当您在C语言中比较两个值时,它们必须是相同的类型。在这种情况下(int和unsigned int),int值首先会被转换为unsigned int。
其次,在C语言中,无符号整数算术运算是模该类型的最大值+1,结果就是“循环”,所以UINT_MAX +1又变成了0,反之亦然。因此,将负值转换为无符号值会产生非常大的数字。
标准中相关的部分说明如下:
"6.3.1.3 有符号整数和无符号整数"
2. 否则,如果新类型是无符号的,则通过重复加上或减去可以在新类型中表示的最大值加1来将值转换到新类型的范围内。

+1 我同意这种转换。花了我一分钟才找到它为什么要在第一次执行时完成(6.3.1.1-p2)。但你在这里描述的“如何”是完全正确的。 - WhozCraig
那其实是错误的部分。 :-) 6.3.1.1/2仅适用于在int/unsigned int的位置上使用的“较小”的类型。 - melpomene

4

当您比较intunsigned int时,int会转换为unsigned int

int转换为unsigned int是通过加上UINT_MAX+1来完成的(请注意,您的int是负数)。所以实际上您正在比较:

if (-3 + UINT_MAX > 10)  //Since -4 is converted to UINT_MAX+1-4

这是正确的。


1
一个int值的第一位用于定义它是正数还是负数。(1 = 负数,0 = 正数) 在比较之前,您的两个变量都被转换为无符号整数,其中第一位的1将被解释为数字的一部分。
这段代码应该可以正常工作:
int main(void) 

 { 

    unsigned int y = 10; 
    int x = – 4; 
    if (x > (int) y) 
    Printf("x is greater");
    else 
    Printf ("y is greater"); 
    getch ( ); 
    return (0); 

 } 

-2

变量 int x=-4 (4 的二进制补码为1111 1100=252),unsigned int y=10(10 的二进制表示为0000 1010=10),所以 252 > 10,因此 -4 大于 10。


如果是这样,int和unsigned int会有什么区别呢? - dilip kumbham
一个int有32位,因此int x = -4实际上在内存中由比252多得多的位表示,即1111 1111 1111 1111 1111 1111 1111 1100(即十六进制表示为0xFFFFFFFC),如果将其解释为无符号整数,则远远超过252。 - Boris Dalstein

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接