在C中投射:陷阱

时间:2017-11-08 16:10:40

标签: c casting

我正在用C语言编写Atmel SAMD20。我遇到了一个错误,我现在已经修复了,但我不确定为什么它首先发生了。有人能指出我吗? (它可能太明显了,我以后会去看脸。)

一组传感器正在生成uint16_t数据,我将其转换为uint8_t以通过I2C发送。所以,这就是我最初的写作方式:

for (i = 0; i < SENSBUS1_COUNT; ++i)
{
  write_buffer[ (i*2) ] = (uint8_t) sample_sensbus1[i] & 0xff;
  write_buffer[(i*2)+1] = (uint8_t) sample_sensbus1[i] >> 8;
}

此处write_bufferuint8_tsample_sensbus1uint16_t

由于某种原因,这最终弄乱了最重要的字节(在大多数情况下,最重要的字节只是1(即0x100))。另一方面,这工作正常,而且应该是它应该是:

for (i = 0; i < SENSBUS1_COUNT; ++i)
{
  write_buffer[ (i*2) ] = sample_sensbus1[i] & 0xff;
  write_buffer[(i*2)+1] = sample_sensbus1[i] >> 8;
}

显然,隐性演员比我聪明。

发生了什么事?

5 个答案:

答案 0 :(得分:4)

write_buffer[(i*2)+1] = ((uint8_t) sample_sensbus1[i]) >> 8;

这相当于:

write_buffer[(i*2)+1] = (uint8_t) (sample_sensbus1[i] >> 8);

如你所见,它会在转变之前进行演员表演。你最重要的字节现在消失了。

但这应该有效:

<=

答案 1 :(得分:4)

您的演员阵容会在转换或掩码之前将uint16_t转换为uint8_t。它被视为你写道:

write_buffer[ (i*2) ] = ((uint8_t)sample_sensbus1[i]) & 0xff;
write_buffer[(i*2)+1] = ((uint8_t)sample_sensbus1[i]) >> 8;

您可能需要:

write_buffer[ (i*2) ] = (uint8_t)(sample_sensbus1[i] & 0xff);
write_buffer[(i*2)+1] = (uint8_t)(sample_sensbus1[i] >> 8);

在实践中,uncast版本也可以。请记住,演员告诉编译器“我比你知道更多关于这件事的事情;按照我说的去做”。如果您不了解编译器,那将是危险的。尽可能避免施放。

您可能还会注意到,按位(或更多)移动(左或右)类型的大小是未定义的行为。但是,((uint8_t)sample_sensbus[i]) >> 8不是未定义的行为,因为“通常的算术转换”意味着(uint8_t)sample_sensbus[i]的结果在转换发生之前转换为int,并且大小为int不能是8位(满足标准必须至少为16位),因此移位不会太大。

答案 2 :(得分:3)

这是运营商优先权的问题。在第一个示例中,您首先转换为uint8_t,然后应用&>>运算符。在第二个示例中,将在隐式转换发生之前应用它们。

答案 3 :(得分:3)

Casting是一元一元的前缀运算符,因此具有很高的优先级。

(uint8_t) sample_sensbus1[i] & 0xff

解析为

((uint8_t)sample_sensbus1[i]) & 0xff

在这种情况下,& 0xff是多余的。但是:

(uint8_t) sample_sensbus1[i] >> 8

解析为

((uint8_t)sample_sensbus1[i]) >> 8

此处强制转换将数字截断为8位,然后>> 8将所有内容都移出。

答案 4 :(得分:3)

问题在于这个表达式:

(uint8_t) sample_sensbus1[i] >> 8; 

它按以下顺序执行:

  1. sample_sensbus1[i]转换为uint8_t,将截断有效地转换为8个最低有效位。这是您丢失数据的地方。
  2. 将上述内容转换为int作为usual arithmetic conversions的一部分,使得只设置了8个较低位的int。
  3. 移动上述int8位,有效地使整个表达式为零。