两个C代码读取寄存器之间的差异



我想知道这两个代码之间的区别。第一个

int32_t lsm6dso32_temperature_raw_get(stmdev_ctx_t *ctx, int16_t *val)
uint8_t buff[2];
int32_t ret;
ret = lsm6dso32_read_reg(ctx, LSM6DSO32_OUT_TEMP_L, buff, 2);
val[0] = (int16_t)buff[1];
val[0] = (val[0] * 256) + (int16_t)buff[0];
return ret;
}

这个

//some code here
uint8_t buffer[14];
data_reg.read(buffer, 14);
rawTemp = buffer[1] << 8 | buffer[0];
//some other code here

第一个代码来自ST,第二个代码来自Adafruit,所有这些代码都是为LSM6DSO326自由度捕获器编写的。我只对字节感兴趣。有什么区别

val[0] = (int16_t)buff[1];
val[0] = (val[0] * 256) + (int16_t)buff[0];

这个

rawTemp = buffer[1] << 8 | buffer[0];

一种解决方案比另一种更好吗?所有这些代码都从捕捉器读取原始温度。

编辑rawTempint16_t

根据rawTemp的类型,这两个方法在定义的行为上可能是相同的,我们没有显示。然而,两者都不是从两个八位字节解码int16_t值的通常正确的方式。

首先考虑这个代码:

val[0] = (int16_t)buff[1];
val[0] = (val[0] * 256) + (int16_t)buff[0];

buff[1]uint8_t,所以它的值范围从到255,所以val[0]*256的范围可以从0到255•256=65280。val[0]int16_t,因此其值范围为−32768到+32767。因此,这个赋值可能会超过CCD_ 11中可以存储的值。在这种情况下,转换的结果是实现定义的。

此外,val[0]*256的类型是int(由于通常的算术转换(。因此,如果int是16位,则val[0]*256可能溢出int中可表示的内容,在这种情况下,行为是未定义的。

考虑此代码:

int16_t rawTemp;
rawTemp = buffer[1] << 8 | buffer[0];

CCD_ 17也可能溢出CCD_。由于行为是未定义的,因此不需要溢出来产生与上述代码相同的结果,特别是因为运算不同(移位与乘法(,并且它以不同的方式与另一个字节组合(逐位OR而不是加法(。

与第一个代码一样,在赋值中可能会发生实现定义的转换。如果是这样,并且赋值的右侧没有溢出(可能是因为int是32位(,那么转换将产生与第一个代码中相同的结果,因为它是由实现定义的。

在没有保证第一个字节小于128或int大于16位并且实现定义的转换封装模216的情况下,这两者都不是从两个8位字节解码int16_t的正确方式。

C在从比特重新组装有符号整数的好方法上是不足的。各种算术和位运算符挂在符号位上并溢出。一种方法是组装一个无符号整数,并将其位复制到一个有符号整数中:

uint16_t t = (uint16_t) buffer[1] << 8 | buffer[0];
int16_t ret;
memcpy(&ret, &t, sizeof ret);

一个好的编译器将生成消除memcpy的代码。

(请注意,以上所有内容都假设有一个小端序表示——较早的字节buffer[0]进入值的低位("小端"(。(

最新更新