uint8_t的可移植重新解释为int8_t并强迫两个人称赞

时间:2019-02-07 21:48:34

标签: c casting stdint

我试图以一种可移植的方式将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)来做到这一点?

3 个答案:

答案 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的位置不使用二进制补码) ),那么该算术方法仍然可以在计算正确的值的意义上起作用,并且可以肯定的是,可以安全地将该值记录在intshort中。因此,提取uint8_t的8位二进制补码解释的 value 的绝对最可移植的方法是

uint8_t u8 = /* ... */;
int i8 = (u8 & 0x7f) - (u8 > 0x7f) * 0x100;

或者,如果您愿意依靠int8_t作为字符类型- ie charsigned 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类型的系统上工作。

在这两种情况下,它们都依赖于实现定义的选择,但是没有人会使用在这些情况下没有做出明显选择的实现(在很大程度上,因为很多现有代码也依赖于这些选择)。有可移植性,然后就是要修改代码以迎合永远不存在的系统。