它应该非常便携,因为当您转换int为unsigned int(通过强制转换)时,您会收到一个值,该值是原始值的 2 的补码位表示,int最高有效位是符号位。
更新:更详细的解释......
我假设其中没有填充位,int并且unsigned int这两种类型中的所有位都用于表示整数值。对于现代硬件来说,这是一个合理的假设。填充位已成为过去,为了向后兼容(即能够在旧机器上运行代码),我们仍然在当前和最近的 C 标准中携带它们。
有了这个假设,如果int和unsigned int有N位(N= CHAR_BIT * sizeof(int)),那么根据 C 标准,我们有 3 个选项来表示int,这是一个有符号类型:
- 符号和大小表示,允许值从 -(2 N-1 -1) 到 2 N-1 -1
- 一个的补码表示,也允许从 -(2 N-1 -1) 到 2 N-1 -1的值
- 二进制补码表示,允许值从 -2 N-1到 2 N-1 -1 或可能从 -(2 N-1 -1) 到 2 N-1 -1
符号和大小和补码表示也已成为过去,但我们暂时不要将它们扔掉。
当我们转换int为unsigned int时,规则是非负值v(>=0) 不变,而负值(<0) 变为 2 N +v的正值,因此= 。v(unsigned int)-1UINT_MAX
因此,(unsigned int)v对于非负数v将始终在 0 到 2 N-1 -1 的范围内,并且最高有效位(unsigned int)v将为 0。
现在,对于v从 到 -2 N-1到 -1 范围内的负数(该范围是 的三种可能表示形式的负数范围的超集int),(unsigned int)v将在 2 N +(-2 N- 1 ) 到 2 N +(-1),简化我们得到从 2 N-1到 2 N -1 的范围。显然,该值的最高有效位将始终为 1。
如果您仔细查看所有这些数学运算,您会发现二进制中的值与2 的补码表示中的值(unsigned)v完全相同:v
...
v= -2: (unsigned)v= 2 N - 2 = 111...110 2
v = -1: (unsigned)v= 2 N - 1 = 111...111 2
v = 0: (unsigned)v= 0 = 000...000 2
v = 1: (unsigned)v= 1 = 000...001 2
...
因此,该值的最高有效位(unsigned)v将为v>=0 的 0 和v<0 的 1。
现在,让我们回到符号和大小和一个的补码表示。这两种表示可以允许两个零, a+0和 a -0。但是算术计算并没有明显区分+0and -0,它仍然是 a 0,无论你加,减,乘还是比较它。你,作为一个观察者,通常不会看到+0或-0与拥有一个或另一个有任何区别。
试图观察和区分+0通常-0是没有意义的,如果你想让你的代码可移植,你通常不应该期望或依赖两个零的存在。
(unsigned int)v不会告诉你 和 之间的区别v=+0,v=-0在这两种情况下(unsigned int)v都等同于0u。
因此,使用这种方法,您将无法判断内部v是 a-0还是 a +0,您不会以这种方式提取 v 的符号位v=-0。
但是同样,您从区分两个零中没有任何实际价值,并且您不希望在可移植代码中进行这种区分。
因此,我敢于声明问题中提出的符号提取方法在实践中非常/非常/相当/等可移植。
不过,这种方法有点矫枉过正。并且(int)v在原始代码中是不必要的,因为v已经是int.
这应该绰绰有余并且易于理解:
int sign = -(v < 0);