我试图以一种可移植的方式将uint8_t重新解释为int8_t(然后再次返回)。我正好通过存储在uint8_t缓冲区中的串行通道接收数据,但是一旦知道了它是哪种数据包,我就需要将某些字节解释为二进制补码,而将其他字节解释为无符号。
我知道这将适用于许多编译器:
int8_t i8;
uint8_t u8 = 0x94;
i8 = (int8_t)u8;
但是不能保证在u8> 127时工作,因为不确定将大于INT8_MAX的值强制转换为int8_t(我认为)。
我能想到的最好的就是这个
int8_t i8;
uint8_t u8;
i8 = (u8 > INT8_MAX) ? (int8_t)(-(256-u8)):(int8_t)u8;
这应该总是有效的,因为减法总是会导致int的自动提升,并且绝不依赖于基础表示。它隐式地迫使值大于INT8_MAX的二进制补码解释。
是否有更好的方法(或标准的MACRO)来做到这一点?
答案 0 :(得分:5)
如果import matplotlib.pyplot as plt
fig,ax = plt.subplots()
# some code...
fig.canvas.toolbar.zoom()
plt.show()
定义了int8_t
,则保证是两个补数(C 2018 7.20.1.1定义了)。
<stdint.h>
中的值可以通过用uint8_t u8
复制到int8_t i8
来重新解释为两个补码值。 (优秀的编译器会对此进行优化,使其仅将memcpy(&i8, &u8, sizeof i8);
用作两个补数,而无需调用u8
。)
答案 1 :(得分:1)
在八位二进制补码中,符号位可以解释为位置值为-2 8 ,当然为-256。实际上,这正是C标准对其进行表征的方式。因此,给定一个存储在uint8_t
中的8位值,您希望将其重新解释为二进制补码整数,这是一种算术方法:
uint8_t u8 = /* ... */;
int8_t i8 = (u8 & 0x7f) - (u8 > 0x7f) * 0x100;
请注意,所有算术都是通过首先将操作数提升为(有符号的)int
来执行的,因此不会发生溢出(因为int
的范围足够大)或无符号的算术环绕。保证算术结果在int8_t
范围内,因此在将结果转换为该类型时也没有溢出的风险。
您将注意到此计算与您的计算之间的相似之处,但是该计算通过直接在算术中使用关系表达式u8 > 0x7f
(0或1)的结果来避免三元运算符,从而避免了任何分支,并且它省去了不必要的演员。 (您也不需要演员。)
还请注意,如果遇到一些不提供int8_t
的怪异实现(因为其char
的宽度大于8位,或者其signed char
的位置不使用二进制补码) ),那么该算术方法仍然可以在计算正确的值的意义上起作用,并且可以肯定的是,可以安全地将该值记录在int
或short
中。因此,提取uint8_t的8位二进制补码解释的 value 的绝对最可移植的方法是
uint8_t u8 = /* ... */;
int i8 = (u8 & 0x7f) - (u8 > 0x7f) * 0x100;
或者,如果您愿意依靠int8_t
作为字符类型- ie char
或signed char
的别名,则它以这种方式完成工作是完全标准的:
uint8_t u8 = /* ... */;
int8_t i8 = *(int8_t *)&u8;
与另一个答案中的memcpy()
替代方案相比,编译器可能更优化了该替代方案,但是与memcpy
替代方案不同,如果{{1 }}证明 not 是字符类型。另一方面,此方法和int8_t
方法都取决于实现提供类型memcpy()
的实现,比不提供int8_t
的实现更不可能实现的是提供{{ 1}}不能是字符类型。
答案 2 :(得分:-1)
i8 = u8;
和i8 = *(int8_t *)&u8;
都可以在任何实际存在并提供int8_t
类型的系统上工作。
在这两种情况下,它们都依赖于实现定义的选择,但是没有人会使用在这些情况下没有做出明显选择的实现(在很大程度上,因为很多现有代码也依赖于这些选择)。有可移植性,然后就是要修改代码以迎合永远不存在的系统。