由于在C中定义转换和操作的方式,使用有符号或无符号变量似乎很少:
uint8_t u; int8_t i;
u = -3; i = -3;
u *= 2; i *= 2;
u += 15; i += 15;
u >>= 2; i >>= 2;
printf("%u",u); // -> 2
printf("%u",i); // -> 2
那么,是否有一套规则可以告诉变量的符号在哪些条件下确实有所作为?
答案 0 :(得分:9)
在这些情况下很重要:
-2/2 = 1
,-2u/2 = UINT_MAX/2-1
,-3%4 = -3
,-3u%4 = 1
>>
和<<
的结果是实现定义或未定义的,resp。对于无符号值,始终定义它们。-2 < 0
,-2u > 0
x+1 > x
始终为真 iff x
具有签名类型。答案 1 :(得分:8)
是。签名将影响C中的大于和小于运算符的结果。请考虑以下代码:
unsigned int a = -5;
unsigned int b = 7;
if (a < b)
printf("Less");
else
printf("More");
在此示例中,“More”输出不正确,因为编译器将-5转换为非常高的正数。
这也会影响您使用不同大小的变量进行算术运算。再次考虑这个例子:
unsigned char a = -5;
signed short b = 12;
printf("%d", a+b);
返回的结果是 263,不是预期的7.这是因为-5实际上被编译器视为251。溢出使您的操作对于相同大小的变量正常工作,但是在扩展时,编译器不会扩展无符号变量的符号位,因此它将它们视为在较大空间中的原始正表示。研究two's compliment的工作原理,你会看到这个结果的来源。
答案 2 :(得分:6)
它会影响您可以存储在变量中的值范围。
答案 3 :(得分:3)
主要是相关的。
printf("%d", (u-3) < 0); // -> 0
printf("%d", (i-3) < 0); // -> 1
答案 4 :(得分:3)
无符号整数上的溢出只是环绕。在签名值上,这是未定义的行为,一切都会发生。
答案 5 :(得分:2)
2的补数的签名只是你解释数字的问题。想象一下3位数字:
000
001
010
011
100
101
110
111
如果你认为000
为零,而数字对于人类来说是自然的,你可以这样解释:
000: 0
001: 1
010: 2
011: 3
100: 4
101: 5
110: 6
111: 7
这称为“无符号整数”。您将所有内容视为大于/等于零的数字。
现在,如果你想让一些数字为负数怎么办?好吧,2的补充来救援。大多数人都知道2的补码只是一个公式,但实际上它只是一致的模2 ^ n其中n是你数字中的位数。
让我举几个一致性的例子:
2 = 5 = 8 = -1 = -4 module 3
-2 = 6 = 14 module 8
现在,为了方便起见,我们假设您决定将数字的最左边一位作为其符号。所以你想拥有:
000: 0
001: positive
010: positive
011: positive
100: negative
101: negative
110: negative
111: negative
查看您的数字全等模2 ^ 3(= 8),您知道:
4 = -4
5 = -3
6 = -2
7 = -1
因此,您将数字视为:
000: 0
001: 1
010: 2
011: 3
100: -4
101: -3
110: -2
111: -1
如您所见,-3和5(例如)的实际位是相同的(如果数字有3位)。因此,撰写x = -3
或x = 5
可以获得相同的结果。
解释数字全等模2 ^ n还有其他好处。如果你总结2个数字,一个负数和一个正数,它可能会在纸上发生,你有一个被丢弃的进位,但结果仍然是正确的。为什么?那个进位是2 ^ n,它与0 modulo 2 ^ n一致!这不方便吗?
溢出也是一致性的另一种情况。在我们的例子中,如果你将两个无符号数字5和6相加,你得到3,实际上是11。
那么,为什么要使用signed和unsigned?对于CPU来说,实际上差别很小。但是你:
因此,例如,如果将-1分配给无符号数,则与为其分配2 ^ n-1相同。
根据您的示例,这正是您正在做的事情。你正在为一个uint8_t分配-3,这是非法的,但就CPU而言,你正在为它分配253。然后所有其余的操作对于两种类型都是相同的,并且最终得到相同的结果。
但有一点是你的例子未命中。在签名号码上的运营商>>
在转移时扩展了标志。由于你的两个操作的结果在转移前都是9,你不会注意到这一点。如果您没有+15,那么i
中有-6,u
中有250,>> 2
-2
会导致i
(如果使用%u,254打印)和u
中的62。 (有关一些技术细节,请参阅下面的Peter Cordes的评论)
为了更好地理解这一点,请以此为例:
(signed)101011 (-21) >> 3 ----> 111101 (-3)
(unsigned)101011 ( 43) >> 3 ----> 000101 ( 5)
如果你注意到,楼层(-21/8)实际上是-3而楼层(43/8)是5.但是,-3和5不相等(并且不是全等模64(64因为有6)比特))