1

在查看各种 sdk 时,LOBYTE 和 HIBYTE 似乎很少一致,如下所示。

视窗

#define LOBYTE(w)           ((BYTE)(((DWORD_PTR)(w)) & 0xff))
#define HIBYTE(w)           ((BYTE)((((DWORD_PTR)(w)) >> 8) & 0xff))

各种 Linux 头文件

#define HIBYTE(w)       ((u8)(((u16)(w) >> 8) & 0xff))
#define LOBYTE(w)       ((u8)(w))

如果将其强制转换为 u8,为什么需要 & 0xff?为什么以下方法不是要走的路?(假设 uint8_t 和 uint16_t 已定义)

#define HIBYTE(w)       ((uint8_t)(((uint16_t)(w) >> 8)))
#define LOBYTE(w)       ((uint8_t)(w))
4

1 回答 1

4

来自ISO/IEC 9899:TC3,6.3.1.3 有符号和无符号整数(在 6.3 转换下):

  1. 当整数类型的值转换为_Bool以外的其他整数类型时,如果该值可以用新类型表示,则保持不变。
  2. 否则,如果新类型是无符号的,则通过在新类型中可以表示的最大值的基础上反复加减一,直到该值在新类型的范围内。

虽然这听起来有点令人费解,但它回答了以下问题。

如果将其强制转换为 u8,为什么需要 & 0xff?

不需要,因为演员会自动进行遮罩。

谈到主题中的问题,OP的最后一个建议是:

#define HIBYTE(w)       ((uint8_t)(((uint16_t)(w) >> 8)))
#define LOBYTE(w)       ((uint8_t)(w))

对于所有无符号值,这将按预期工作。有符号值将始终由宏转换为无符号值,在二进制补码的情况下不会改变表示,因此计算结果定义明确。然而,假设二进制补码是不可移植的,因此该解决方案对于有符号整数不是严格可移植的。

为有符号整数实现一种可移植的解决方案将非常困难,甚至可能会质疑这种实现的含义:

  • 结果应该是签名还是未签名?
  • 如果结果应该是无符号的,它实际上并不符合初始数字的高/低字节,因为可能需要更改表示来获得它。
  • 如果结果应该签名,则必须进一步指定。>>例如,负值的结果是实现定义的,因此获得可移植的明确定义的“高字节”听起来很有挑战性。人们应该真正质疑这种计算的目的。

由于我们正在扮演语言律师,我们可能想知道(uint16_t)(w) >> 8. 无符号似乎是显而易见的答案,但事实并非如此,因为整数提升规则。

除其他外,整数提升适用于如下指定的对象或表达式。

整数类型的对象或表达式,其整数转换等级小于或等于 int 和 unsigned int 的等级。

这种情况下的整数提升规则指定为:

如果一个 int 可以表示原始类型的所有值,则将该值转换为 int;

典型的 32 位或 64 位机器上的左操作数就是这种情况。

幸运的是,在这种情况下,转换后的左操作数仍然是非负数,这使得结果>>定义明确:

E1 >> E2 的结果是 E1 右移 E2 位位置。如果 E1 具有无符号类型或 E1 具有有符号类型和非负值,则结果的值是 E1 / 2 E2的商的整数部分。

于 2021-07-01T21:51:15.590 回答