获取 16 位整数的低字节或高字节的最安全 cross-platform 方法是什么?
What is the safest cross-platform way to get the low byte or the high byte of a 16-bit integer?
在查看各种 sdk 时,LOBYTE 和 HIBYTE 似乎很少一致,如下所示。
Windows
#define LOBYTE(w) ((BYTE)(((DWORD_PTR)(w)) & 0xff))
#define HIBYTE(w) ((BYTE)((((DWORD_PTR)(w)) >> 8) & 0xff))
各种LinuxHeaders
#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))
来自 ISO/IEC 9899:TC3,6.3.1.3 有符号和无符号整数(在 6.3 转换下):
- When a value with integer type is converted to another integer type other than _Bool, if the value can be represented by the new type, it
is unchanged.
- Otherwise, if the new type is unsigned, the value is converted by repeatedly adding or subtracting one more than the maximum value that
can be represented in the new type until the value is in the range of
the new type.
虽然这听起来有点令人费解,但它回答了以下问题。
Why is & 0xff needed if it's cast to a u8?
不需要,因为演员会自动进行遮蔽。
关于题目中的问题,OP最后的建议是:
#define HIBYTE(w) ((uint8_t)(((uint16_t)(w) >> 8)))
#define LOBYTE(w) ((uint8_t)(w))
对于所有无符号值,这将按预期工作。有符号值将始终由宏转换为无符号值,在二进制补码的情况下不会改变表示形式,因此计算结果定义明确。但是,假设二进制补码不可移植,因此该解决方案对于带符号的整数不是严格可移植的。
为有符号整数实施一种可移植的解决方案将非常困难,甚至有人会质疑这种实施的意义:
- 结果应该是有符号的还是无符号的?
- 如果结果应该是无符号的,它实际上不符合初始数字的 high/low 字节,因为可能需要更改表示才能获得它。
- 如果要对结果进行签名,则必须进一步指定。例如,负值的
>>
的结果是实现定义的,因此获得可移植的定义明确的“高字节”听起来很有挑战性。真的应该质疑这样一种计算的目的。
由于我们扮演的是语言律师,我们可能想知道 (uint16_t)(w) >> 8
的左操作数的符号性。无符号似乎是显而易见的答案,但由于整数提升规则,事实并非如此。
除其他外,整数提升适用于如下指定的对象或表达式。
An object or expression with an integer type whose integer conversion rank is less than or equal to the rank of int and unsigned int.
这种情况下的整数提升规则指定为:
If an int can represent all values of the original type, the value is converted to an int;
典型的 32 位或 64 位机器上的左操作数就是这种情况。
幸运的是在这种情况下,转换后的左操作数仍然是非负的,这使得 >>
的结果定义明确:
The result of E1 >> E2 is E1 right-shifted E2 bit positions. If E1 has an unsigned type or if E1 has a signed type and a nonnegative value, the value of the result is the integral part of the quotient of E1 / 2E2.
在查看各种 sdk 时,LOBYTE 和 HIBYTE 似乎很少一致,如下所示。
Windows
#define LOBYTE(w) ((BYTE)(((DWORD_PTR)(w)) & 0xff))
#define HIBYTE(w) ((BYTE)((((DWORD_PTR)(w)) >> 8) & 0xff))
各种LinuxHeaders
#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))
来自 ISO/IEC 9899:TC3,6.3.1.3 有符号和无符号整数(在 6.3 转换下):
- When a value with integer type is converted to another integer type other than _Bool, if the value can be represented by the new type, it is unchanged.
- Otherwise, if the new type is unsigned, the value is converted by repeatedly adding or subtracting one more than the maximum value that can be represented in the new type until the value is in the range of the new type.
虽然这听起来有点令人费解,但它回答了以下问题。
Why is & 0xff needed if it's cast to a u8?
不需要,因为演员会自动进行遮蔽。
关于题目中的问题,OP最后的建议是:
#define HIBYTE(w) ((uint8_t)(((uint16_t)(w) >> 8)))
#define LOBYTE(w) ((uint8_t)(w))
对于所有无符号值,这将按预期工作。有符号值将始终由宏转换为无符号值,在二进制补码的情况下不会改变表示形式,因此计算结果定义明确。但是,假设二进制补码不可移植,因此该解决方案对于带符号的整数不是严格可移植的。
为有符号整数实施一种可移植的解决方案将非常困难,甚至有人会质疑这种实施的意义:
- 结果应该是有符号的还是无符号的?
- 如果结果应该是无符号的,它实际上不符合初始数字的 high/low 字节,因为可能需要更改表示才能获得它。
- 如果要对结果进行签名,则必须进一步指定。例如,负值的
>>
的结果是实现定义的,因此获得可移植的定义明确的“高字节”听起来很有挑战性。真的应该质疑这样一种计算的目的。
由于我们扮演的是语言律师,我们可能想知道 (uint16_t)(w) >> 8
的左操作数的符号性。无符号似乎是显而易见的答案,但由于整数提升规则,事实并非如此。
除其他外,整数提升适用于如下指定的对象或表达式。
An object or expression with an integer type whose integer conversion rank is less than or equal to the rank of int and unsigned int.
这种情况下的整数提升规则指定为:
If an int can represent all values of the original type, the value is converted to an int;
典型的 32 位或 64 位机器上的左操作数就是这种情况。
幸运的是在这种情况下,转换后的左操作数仍然是非负的,这使得 >>
的结果定义明确:
The result of E1 >> E2 is E1 right-shifted E2 bit positions. If E1 has an unsigned type or if E1 has a signed type and a nonnegative value, the value of the result is the integral part of the quotient of E1 / 2E2.