如何在C中将2个字节转换为带符号的short

时间:2019-06-22 14:51:55

标签: c bit byte-shifting

我有2个字节需要转换为带符号的短号。例如,我有单独的二进制字节(0000 0001)和(0000 0002)。如何将它们转换为带符号的short值?

3 个答案:

答案 0 :(得分:2)

如果字节以带符号的数据类型(例如signed charint8_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或更高版本编译,设法将其简化为简单的三指令序列,而没有乘法或条件。)

答案 1 :(得分:1)

给出:

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

将导致lsb = -32513,而如果在实现中签署了msbchar并且char,则结果将是由于隐式类型提升和lsb的符号扩展,因此为-1。

这仍然没有严格定义,因为左侧表达式提升为unsigned int并可能导致值无法表示为int16_t,在这种情况下,行为是实现定义的。就是说,这是不寻常的实现,它不执行任何其他操作,而后仅逐字复制位,这就是为什么它起作用的原因,并且上面的操作很惯用。

如果明确要求使用short,则无论short的长度如何,为了保证正确签名的结果,您可以显式转换为int16_t并分配给short(甚至是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 ;

https://onlinegdb.com/Byth1N3yr

答案 2 :(得分:0)

假设0x01是MSB,0x02是LSB,那么unsigned short foo = 0x01 << 8 | 0x02;就足够了。但是,这意味着unsigned short至少为16位(取决于实现,请搜索stdint.h以获取固定大小)