我做了一些快速测试,在 C 中unsigned int
转换signed int
不会更改位值(在在线调试器上(。
我想知道的是它是由 C 标准保证还是只是常见(但不是 100% 确定(行为?
从signed int
到unsigned int
的转换不会改变二进制补码 C 实现中的位表示,这是最常见的,但会改变负数的位表示,包括补码或符号和幅度系统上可能的负零。
这是因为强制转换(unsigned int) a
未定义为保留位,但结果是将a
除以UINT_MAX + 1
的正余数(或如 C 标准 (C11 6.3.1.3p2( 所述,
通过重复添加或减去比新类型中可以表示的最大值多一个值来转换该值,直到该值在新类型的范围内。
负数的两者补码表示是有符号数最常用的表示形式,正是因为它具有负值的属性n
映射到与数学值相同的位模式n + UINT_MAX + 1
- 它可以使用相同的机器指令进行有符号和无符号加法,并且负数将由于环绕而起作用。
需要从有符号整数转换为无符号整数才能生成正确的算术结果(相同的数字(,可以说是无符号整数的大小取模。 也就是说,在之后
int i = anything;
unsigned int u = (unsigned int)i;
而在具有 32 位整数的机器上,要求是u
等于i
,模数 232。
(我们也可以尝试说u
接收值i % 0x100000000
,但事实证明这不太正确,因为 C 规则说,当你将负整数除以正整数时,你会得到一个四舍五入到 0 的商和一个负余数,这不是我们在这里想要的那种模。
如果i
为 0 或正数,则不难看出u
将具有相同的位模式。 如果i
为负数,并且如果您在 2 的补码机上,则结果也保证具有相同的位模式。 (我很想在这里为这个结果提供一个很好的证明,但我现在没有时间尝试构建它。
今天的绝大多数机器都使用2的补语。 但是如果你在 1 的补码或符号/幅度机器上,我很确定位模式并不总是相同的。
因此,归根结底,C 标准不保证位模式的相同性,而是由于 C 标准的要求和 2 的补码算法细节的组合而产生的。