signed int x = -5;
unsigned int y = x;
的价值是y
多少?这是怎么回事?
这取决于 的最大值unsigned int
。通常,aunsigned int
是 32 位长,因此UINT_MAX
是 2 32 - 1。C 标准(第 6.3.1.3/2 节)要求执行有符号 → 无符号转换
否则,如果新类型是无符号的,则通过在新类型中可以表示的最大值的基础上反复加减一,直到该值在新类型的范围内。
因此 y = x + ((2 32 - 1) + 1) = 2 32 - 5 = 4294967291。
在当今大多数实现的2 的补码平台中,也与 .的 2 的补码表示相同。y
x
-5 = ~5 + 1 = 0xFFFFFFFA + 1 = 0xFFFFFFFB = 4294967291。
来自 C99 标准:
6.3.1.3 有符号和无符号整数
- 当整数类型的值转换为_Bool以外的其他整数类型时,如果该值可以用新类型表示,则保持不变。
- 否则,如果新类型是无符号的,则通过在新类型中可以表示的最大值的基础上反复加减一,直到该值在新类型的范围内。49)
49) 规则描述的是数学值的算术,而不是给定类型表达式的值。
因此,您将有效地查看y = x + UINT_MAX + 1
.
这恰好意味着二进制补码表示形式不变地用作无符号整数,这在大多数现代计算机上非常快,因为它们对有符号整数使用二进制补码。
的值为,y
即。UINT_MAX - 5 + 1
UINT_MAX - 4
当您将有符号整数值转换为无符号类型时,该值以 2^N 为模减少,其中 N 是无符号类型中值形成位的数量。这适用于负符号和正符号值。
如果要从有符号类型转换为相同大小的无符号类型,则上述表示正符号值保持不变(例如转换+5
为转换为)。5
MAX + 1
MAX
-5
MAX + 1 - 5
有符号值通常存储为所谓的二进制补码:
二进制补码是一种将负数编码为普通二进制的方法,这样加法仍然有效。加 -1 + 1 应该等于 0,但普通加法会给出 2 或 -2 的结果,除非该操作特别注意符号位并改为执行减法。没有这个额外的步骤,二进制补码会产生正确的和。
这意味着数字 -5 和 4294967291 在内存中的实际表示(对于 32 位字)是相同的,例如:0xFFFFFFFB
或0b11111111111111111111111111111011
。所以当你这样做时:
unsigned int y = x;
x 的内容被逐字复制,即按位复制到y
. 这意味着如果您检查内存中的原始值,x
它们y
将是相同的。但是,如果您这样做:
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
z
z1
将等于 0 但不会z2
。这可以通过在扩展之前将值强制转换为已签名来解决:
long long z3 = (long long)(signed int)y + 5;
或类似地,如果您不希望出现符号扩展:
long long z4 = (long long)(unsigned int)x;
y=0xfffffffb 它是 -5 的二进制表示(二进制补码)