【问题标题】:Casting uint8_t to int8_t将 uint8_t 转换为 int8_t
【发布时间】:2015-11-25 13:38:46
【问题描述】:

使用 Microchip XC8 我有一个构造

int16_t test;
uint8_t msb = 0xff;
uint8_t lsb = 0xf4;
uint8_t hyst = 0xff; 

test = ((((int16_t)msb)<<8) + (int16_t)lsb) + (int8_t)hyst);

我希望 test 为 -13 (oxFFF3),因为我使用了带符号的数字,但它被计算为 0xF3。

为什么?

编辑:使用 gcc 进行了尝试,结果符合我的预期。

【问题讨论】:

  • 标准整数转换和未定义行为。
  • 如果 int 由于左移而为 16 位,则这是未定义的行为。 sizeof(int) 是什么?
  • 然后是 UB。如果您将((int16_t)msb) 更改为((uint16_t)msb),您将摆脱未定义的行为,尽管它仍然是实现定义的。
  • 转换为 int16_t 是多余的,只会降低可读性,因为这些值无论如何都会被提升为 int。

标签: c microchip xc8


【解决方案1】:

正如 Olaf 正确指出的那样,如果 int 的宽度为 16,则移位操作可能没有定义的行为,因为您将一位移位到 int16_t 的符号位。那么你的代码就是错误的,你无法推断出你的平台可能实现的价值。

如果int 更大,则表达式一切正常,您只需将适合int 的正值相加。但是由于初始化,结果值将被转换回int16_t,这会导致“实现定义”转换或信号的提升。所以你应该检查你的编译器文档你的平台在这些情况下做了什么,如果可以的话最好避免它。

【讨论】:

  • 只有int为16位时才不确定,因为整数提升是在移位之前进行的。
  • @interjay,查看我的编辑,在另一种情况下,UB 正在转换为int16_t
  • 如果 int 较大,则转换为 int16_t 将由实现定义。
  • 如果在从intint16_t 的转换中出现溢出,则溢出的行为是实现定义的(它可以是实现定义的信号,但不是一个非常通常在现代架构上,甚至是嵌入式架构上)。
  • 出于某种原因,您喜欢使用“没有定义的行为”之类的迂回说法来非法使用带符号的左移,但在标准中明确将其命名为“未定义的行为”。作为坚持让其他人使用 mot juste (openwall.com/lists/musl/2015/07/17/2) 的人,将其称为标准中的名称会更简单。
【解决方案2】:

来自 cmets 的确定

int16_t test;
uint8_t msb = 0xff;
uint8_t lsb = 0xf4;
uint8_t hyst = 0xff; 

test = (int16_t)(((uint16_t)msb)<<8) + (uint16_t)lsb) + (int8_t)hyst;

解决问题。谢谢各位!

【讨论】:

  • 一般来说,你不应该混用有符号和无符号。如果您组装位域,通常使用无符号的方式。
猜你喜欢
  • 2022-11-12
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-06-10
  • 1970-01-01
相关资源
最近更新 更多