将uint16_t解释为int16_t

Interpreting a uint16_t as a int16_t

本文关键字:int16 解释 uint16      更新时间:2023-10-16

是否有一种可移植且安全的方法将boost::uint16_t生成的位模式解释为boost::int16_t?我有一个uint16_t,我知道它表示一个编码为小端序的有符号16位整数。我需要对这个值做一些有符号的算术运算,所以有没有办法说服编译器它已经是一个有符号的值?

如果我没有弄错的话,static_cast<int16_t>会转换这个值,也许会改变它的比特模式。

如果您正在寻找与强制转换不同的东西,请将其内存表示复制到boost::int16_t的内存表示,因为它从一开始就代表什么。

编辑:如果必须让它在big-endian机器上工作,只需向后复制字节即可。使用std::copystd::reverse

只需使用静态强制转换。如果你所在的平台对比特模式的定义不同,那么改变比特模式恰好是你想要的。

interpret_cast或任何等效的指针强制转换都是未定义的(未定义实现(。这意味着编译器可以自由地做一些讨厌的事情,比如将未定义的表单缓存在寄存器中,然后错过更新。此外,如果你在一个比特模式不同的平台上,那么绕过转换会给你留下垃圾(就像假装float是int并加1一样(

更多信息在C中的有符号到无符号转换-它总是安全的吗?但是,摘要C以一种迂回的方式,将静态强制转换(实际上是普通的C强制转换(定义为通过在x86(使用2的补码(上相同地处理位而得到的

不要对编译器耍花招(这在这个编译器上总是有效的,所以他们肯定不会通过更改来破坏每个人的代码(。历史证明你错了。

屏蔽除符号位之外的所有符号位,将其存储在带符号的int中,然后使用符号位设置符号。

我想*(boost::int16_t*)(&signedvalue)会起作用,除非您的系统体系结构默认情况下不是小端序。endian-ness将改变行为,因为在上述操作之后,cpu将把有符号的值视为体系结构特定的boost::int16_t值(这意味着如果您的体系结构是big-endian,它就会出错(。

编辑
为了避免对*(int16_t)(&input_value)的争议,我将代码块中的最后一条语句更改为memcpy,并添加了*(int16_t)(&input_value)作为附录。(相反(。

在大端序机器上,您需要进行字节交换,然后将其解释为有符号整数:

if (big_endian()) {
  input_value = (uint16_t)((input_value & 0xff00u) >> 8) |
                (uint16_t)((input_value & 0x00ffu) << 8);
}
int16_t signed_value;
std::memcpy (&signed_value, &input_value, sizeof(int16_t));

在大多数计算机上,您可以将呼叫从memcpy更改为signed_value = *(int16_t)(&input_value);。严格来说,这是未定义的行为。它也是一个使用极其广泛的成语。几乎所有的编译器都用这个语句做了"正确的事情"。但是,正如YMMV语言的扩展一样。

作为一种不同的策略,最大化(但不确保(可移植性的最佳方法是将这些有符号的16位整数存储为网络顺序的有符号16位整数,而不是小端顺序的无符号16位整型。这给目标机器带来了负担,使其能够将这些16位网络顺序有符号整数转换为目标机器的本地形式的16位有符号整数。并不是每台机器都支持这一功能,但大多数可以连接到网络的机器都支持。毕竟,该文件必须通过某种机制到达目标机器,因此它很有可能理解网络顺序。

另一方面,如果你通过某个专有的串行接口将二进制文件转移到某个嵌入式机器上,那么可移植性问题的答案与你告诉医生"我这样做很痛苦"时得到的答案相同