【问题标题】:What is the safest cross-platform way to get the low byte or the high byte of a 16-bit integer?获取 16 位整数的低字节或高字节的最安全的跨平台方法是什么?
【发布时间】:2021-09-13 22:20:28
【问题描述】:

在查看各种 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))

【问题讨论】:

  • 添加了一些标签,因为如果这不是是为了避免任何偷偷摸摸的陷阱,那对于 Stack Overflow 来说太主观了。
  • 注意uint8_t、uint16_t等是可选的;实现甚至不需要具有这些特定宽度的整数类型。 (例如unsigned char 不必是 8 位宽,并且存在 9 位或 32 位的系统。)因此它们不能用于真正的“跨平台”解决方案。
  • @NateEldredge 假设它们已定义。
  • "如果转换为 u8,为什么需要 & 0xff?" --> Belt and suspenders。单独使用(uint8_t) 更可取,因为它定义了类型。
  • 进一步注意u8 和u16 是可选的uint8_t 和uint16_t 的进一步可选类型定义。 uint8_t 和 uint16_t 更有可能通过 stdint.h 标头提供。鉴于您的所有三个示例,带有uint8_t 和uint16_t 的示例是最便携的。

标签: c language-lawyer platform-agnostic


【解决方案1】:

来自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-09-14
  • 2011-02-02
  • 1970-01-01
  • 2019-02-22
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-07-06
相关资源
最近更新 更多