强制转换 (int) 和 (int16_t) 时 C++ 中的含义是什么

what is the meaning in C++ when casting both (int) and (int16_t)

我正在研究 Arduino 示例中用于读取外部数据的 C++ 代码。 该代码使用转换 (int16_t) 和 (int).

int16_t是固定整数类型,但我不明白它在代码中的用途。

_vRaw[0] = (int)(int16_t)(Wire.read() | Wire.read() << 8);

我这样写有什么区别吗?

_vRaw[0] = (int)(Wire.read() | Wire.read() << 8);

(int16_t) 转换会将值截断为 16 位。 (int) 转换什么都不做,除非 _vRaw[0] 有一个类型可以从 int 而不是 int16_t 隐式转换,这是不太可能的。

很可能 Wire.read() returns char,并且这些转换都不做任何事情,因为 (Wire.read() | Wire.read() << 8) 已经是一个 int 表达式16位以内。

我以前写过这样的代码,所以我可以向 int16_t 解释转换的目的。

Arduino命令Wire.read()通常returns一个0到255之间的数字代表从I2C设备读取的一个字节。

有时,您从设备读取的两个字节将使用 Two's complement 表示一个带符号的 16 位数字。因此,例如,如果第一个字节为 1,第二个字节为 2,我们希望将其解释为值 1 + 2*256 = 513。如果两个字节均为 255,我们希望将其解释为 -1。

让我们假设您的代码是为 int 是 32 位的系统(几乎任何 32 位微控制器)编译的,并且两个字节都是 255,并考虑这个表达式:

(Wire.read() | Wire.read() << 8)

这个表达式的值只是 255 | (255 << 8),即 65535。这很糟糕;我们希望值为 -1。

修复该错误的简单方法是向 int16_t 添加强制转换:

(int16_t)(Wire.read() | Wire.read() << 8)

当我们将 65535 转换为 int16_t 类型时,我们得到 -1,因为 int16_t 不能容纳 65535(它只能容纳从 -32768 到 32767 的值)。 (C++ 标准不保证我们得到 -1,但它是实现定义的行为,所以如果你想确定,你可以检查 manual of your compiler。)如果我们稍后将它转换回 int (通过显式转换或通过设置一些 int 等于它),编译器将执行正确的签名扩展操作并给我们一个值为 -1 的 int

顺便说一下,我不确定您的编译器是否会 运行 以正确的顺序对 Wire.read() 的两次调用,除非您在这两个调用之间有一个分号。此外,可能不需要最终转换为 int。所以我会把代码重写成这样:

uint8_t lsb = Wire.read();
_vRaw[0] = (int16_t)(lsb | Wire.read() << 8);