C(输出)中的无符号和有符号值

signed int x = -5; unsigned int y = x; 

y的价值是多少? 怎么会这样?

它取决于unsigned int的最大值。 通常, unsigned int为32位长,因此UINT_MAX为2 32 – 1.C标准(§6.3.1.3/ 2)要求执行带符号→无符号转换

否则,如果新类型是无符号的,则通过重复地添加或减去一个可以在新类型中表示的最大值来转换该值,直到该值在新类型的范围内。

因此y = x +((2 32-1 )+ 1)= 2 32-5 = 4294967291。


在2的补码平台中,大多数实现都是如此y也与x 2的补码表示相同。

-5 = ~5 + 1 = 0xFFFFFFFA + 1 = 0xFFFFFFFB = 4294967291。

从C99标准:

6.3.1.3有符号和无符号整数

  1. 当具有整数类型的值转换为除_Bool之外的另一个整数类型时,如果该值可以由新类型表示,则它将保持不变。
  2. 否则,如果新类型是无符号的,则通过重复地添加或减去一个可以在新类型中表示的最大值来转换该值,直到该值在新类型的范围内。 49)

49)规则描述了数学值的算术,而不是给定类型表达式的值。

因此,您将有效地查看y = x + UINT_MAX + 1

这恰好意味着二进制补码表示不作为无符号整数使用,这使得它在大多数现代计算机上都非常快,因为它们对有符号整数使用二进制补码。

y的值是UINT_MAX - 5 + 1 ,即UINT_MAX - 4

将有符号整数值转换为无符号类型时,该值将以模2 ^ N的forms减少,其中N是无符号类型中的值形成位数。 这适用于负值和正值有符号值。

如果要从有符号类型转换为相同大小的无符号类型,则上述意味着正有符号值保持不变(例如, +5转换为5 ),负值被添加到MAX + 1 ,其中MAX是最大值无符号类型的值( -5转换为MAX + 1 - 5 )。

有符号值通常存储为称为二进制补码的东西:

二进制补码是将负数编码为普通二进制数的一种方法,因此加法仍然有效。 添加-1 + 1应该等于0,但普通加法给出2或-2的结果,除非操作特别注意符号位并执行减法。 如果没有这个额外的步骤,则二进制补码得到正确的总和。

这意味着数字-5和4294967291在存储器(对于32位字)的实际表示是相同的,例如: 0xFFFFFFFB0b11111111111111111111111111111011 。 所以当你这样做时:

 unsigned int y = x; 

x的内容是逐字复制的,即按位复制到y 。 这意味着如果检查xy内存中的原始值,它们将是相同的。 但是如果你这样做:

 unsigned long long y1 = x; 

x的值将在转换为unsigned long long之前进行符号扩展。 在long long为64位的常见情况下,这意味着y1等于0xFFFFFFFFFFFFFFFB

重要的是要注意当铸造到更大的类型时会发生什么。 将转换为更大的签名值的签名值将进行符号扩展。 如果源值是无符号的,则不会发生这种情况,例如:

 unsigned int z = y + 5; long long z1 = (long long)x + 5; // sign extended since x is signed long long z2 = (long long)y + 5; // not sign extended since y is unsigned 

zz1将等于0但z2不会。 这可以通过扩展之前将值转换为signed来解决:

 long long z3 = (long long)(signed int)y + 5; 

或类似地,如果您希望发生符号扩展:

 long long z4 = (long long)(unsigned int)x; 

y = 0xfffffffb它是-5的二进制表示(二进制补码)