我有 2 个字节需要转换为有符号短数。例如,我在二进制中有单独的字节 (0000 0001) 和 (0000 0002)。如何将这些转换为带符号的短值?
3 回答
如果字节保存在带符号的数据类型中,例如signed char
or int8_t
,那么它非常简单:
signed short combine_signed(signed char byte1, signed char byte2) {
return byte1 * 256 + (uint8_t)byte2;
}
这里使用乘法,而不是移位操作,但预计编译器实际上会插入适当的移位操作。C 标准没有指定左移负数的结果,因此在可移植代码中不能使用左移。
如果字节是无符号类型或宽于 8 位的类型,那么最简单的方法是首先将高位字节转换为有符号值,然后按上述方法进行操作。转换为有符号值不能通过简单的强制转换来完成,因为这样的转换将是整数溢出,其结果未由 C 标准指定。所以一个可移植的程序必须明确地测试高位:
signed short combine(int byte1, int byte2) {
// This code assumes that byte1 is in range, but allows for the possibility
// that the values were originally in a signed char and so is now negative.
if (byte1 >= 128) byte1 -= 256;
return byte1 * 256 + (uint8_t)byte2;
}
(x86 的 gcc 和 clang 都使用 -O2 或更好的版本编译,设法将其简化为一个简单的三指令序列,没有乘法或条件。)
鉴于:
char msb = 0x01 ;
char lsb = 0x02 ;
然后:
short word = (msb << 8) | (lsb & 0xff) ;
将导致word
值为 0x0102 (或 258 10)。
由于您要求签署短片,但这不是一个非常有趣的例子。为了:
char msb = 0x80 ;
char lsb = 0x02 ;
word
将有 0x8002,对于 16 位short
将是 -32766。
但是,在short
长度超过 16 位(允许)的实现中,结果将被解释为 +32770。在这种情况下,使用int16_t
定义的固定大小的类型stdint.h
来避免任何潜在的实现依赖性要安全得多。
int16_t word = (msb << 8) | (lsb & 0xff) ;
这可以通过使用uint8_t
而不是char
可以有符号或无符号来稍微简化:
uint8_t msb = 0x80u ;
uint8_t lsb = 0xFFu ;
int16_t word = (msb << 8) | lsb ;
将导致word
= -32513,而 iflsb
和msb
werechar
和char
were 在实现中签名,那么由于隐式类型提升和lsb
.
这仍然没有严格定义,因为左侧表达式提升unsigned int
并可能导致无法表示为 a 的值int16_t
,并且在这种情况下,行为是实现定义的。这就是说,这将是一个不寻常的实现,除了简单地逐字复制位之外的任何其他操作,这就是它起作用的原因,上面是惯用的。
如果short
明确要求,无论 的长度如何,为了保证正确签名的结果short
,您可以显式转换int16_t
并分配给 a short
(甚至是 a int
):
short word = (int16_t)((msb << 8) | (lsb & 0xFF));
也可以使用联合解决方案,但考虑到这个问题的标签,在这种情况下它似乎不太可能是一个可接受的解决方案。它具有避免任何实现定义的行为和神秘的类型提升和隐式转换规则的优点,但您必须处理字节序:
#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
#define LSB 0
#define MSB 1
#else
#define LSB 1
#define MSB 0
#endif
union
{
int16_t word ;
uint8_t byte[2] ;
} reinterpret ;
reinterpret.byte[MSB] = 0x80u ;
reinterpret.byte[LSB] = 0xFFu ;
short word = reinterpret.word ;
假设 0x01 是 MSB,0x02 是 LSB,那么unsigned short foo = 0x01 << 8 | 0x02;
就足够了。但是,这意味着它unsigned short
至少是 16 位的(这取决于实现,搜索 stdint.h 以获得固定大小)