请查看以下代码:
#include <stdlib.h>
#include <stdio.h>
int main()
{
unsigned int a = 1;
unsigned int b = -1;
printf("0x%X\n", (a-b));
return 0;
}
结果是0x2。
我认为整数提升不应该发生,因为“a”和“b”的类型都是unsigned int。但结果打败了我......我不知道原因。
顺便说一下,我知道算术结果应该是2,因为1 - ( - 1)= 2。但是b的类型是unsigned int。将(-1)赋值给b时,b的值实际上是0xFFFFFFFF。它是unsigned int的最大值。当一个小的无符号值减去一个大值时,结果不是我所期望的。
从下面的答案中,我认为溢出可能是一个很好的解释。 现在我写了其他测试代码。它证明了溢出的答案是正确的。
#include <stdlib.h>
#include <stdio.h>
int main()
{
unsigned int c = 1;
unsigned int d = -1;
printf("0x%llx\n", (unsigned long long)c-(unsigned long long)d);
return 0;
}
结果是“0xffffffff00000002”。这是我期待的。
答案 0 :(得分:8)
unsigned int a = 1;
这会将a
初始化为1.实际上,由于1
属于int
类型,因此存在隐式int
- 至 - unsigned
转换,但它是一个微不足道的转换,不会改变价值或代表性。)
unsigned int b = -1;
这更有趣。 -1
的类型为int
,初始化会隐式将其从int
转换为unsigned int
。由于数学值-1
无法表示为unsigned int
,因此转换非常重要。这种情况下的规则是(引用C标准第6.3.1.3节):
通过重复添加或减去一个来转换该值 比直到新类型可以表示的最大值 该值在新类型的范围内。
当然,只要结果相同,它实际上不必这样做。将UINT_MAX+1
(“可以在新类型中表示的最大值之一”)添加到-1会产生UINT_MAX
。 (该添加是以数学方式定义的;它本身不受任何类型转换的影响。)
实际上,将-1
分配给任何无符号类型的对象是获取该类型的最大值的好方法,而无需引用*_MAX
中定义的<limits.h>
宏
因此,假设一个典型的32位系统,我们有a == 1
和b == 0xFFFFFFFF
。
printf("0x%X\n", (a-b));
减法的数学结果为-0xFFFFFFFE
,但这明显超出unsigned int
的范围。无符号算术的规则类似于无符号转换的规则;结果是2。
答案 1 :(得分:3)
谁说你正在进行整数推广?让我们假装你的整数是两个补码(可能,虽然不是标准规定的),它们只有四位宽(根据标准不可能,我只是用它来简化事情)。
int unsigned-int bit-pattern
--- ------------ -----------
1 1 0001
-1 15 1111
------
(subtract with borrow) 1 0010
(only have four bits) 0010 -> 2
您可以在此处看到,您可以获得所看到的结果,而无需升级为已签名或更广泛的类型。
答案 2 :(得分:2)
如果减去负数,则相当于添加正数。
a = 1
b = -1
(a-b) = ?
((1)-(-1)) = ?
(1-(-1)) = ?
(1+1) = ?
2 = ?
首先,您可能认为这是不允许的,因为您指定了unsigned int
;但是,您还要将signed int
(-1常量)转换为unsigned int
。因此,您实际上将完全相同的位存储到unsigned int(0xFFFF)中。
然后,在表达式中,你取0xFFFF值的负数,当然强制要签名的数字。实际上,您正在逐步规避unsigned
指令。
答案 3 :(得分:2)
应该有一个编译器警告你可能忽略或关闭了,但是仍然可以将-1存储在无符号整数中。在内部,-1作为0xffffffff存储在32位机器上。因此,如果从1中减去0xffffffff,最终会得到-0xfffffffe,即2.(没有负数,负数是最大整数值加1减去数字)。
底线 - 有符号或无符号无关紧要,只有在你比较数值时它才会起作用。
从数学上讲,1 - (-1)= 1 + 1。