对于以下代码,我得到了溢出,但遗憾的是我似乎无法理解为什么。
std::int8_t smallValue = -1;
unsigned int value = 500;
std::uint8_t anotherSmallValue = 1;
auto test = smallValue * value * anotherSmallValue;
之后test
是一个相当大的值。
有人可以解释一下,这里会发生什么吗?
对于以下代码,我得到了溢出,但遗憾的是我似乎无法理解为什么。
std::int8_t smallValue = -1;
unsigned int value = 500;
std::uint8_t anotherSmallValue = 1;
auto test = smallValue * value * anotherSmallValue;
之后test
是一个相当大的值。
有人可以解释一下,这里会发生什么吗?
当编译器看到smallValue * value
时,它必须根据输入数据类型signed
(8 位)和unsigned int
(通常是 16 位或 32 位)决定结果的数据类型。C++ 的规则规定,在这种情况下,结果将是无符号的。因此, 的值smallValue * value
不能是-500
,正如您所期望的那样;相反,该值-500
被解释为一个正数。
此外,您在这里将一个 8 位值乘以一个通常为 16 位或 32 位的值。在这种情况下,C++ 的规则规定,较小的存储值将首先被转换为与较大的相同的大小;所以在这种情况下,结果smallValue * value
确实会大到足以存储一个数量级500
。
继续乘以无符号数量anotherSmallValue
(=1) 会导致另一个unsigned
具有相同值的数量。
因为你正在使用auto
,所以返回类型被推断为是unsigned
。
简单地通过转换回 a signed
(例如,通过将值定义test
为 an int
,而不是 an auto
,通常会将整个操作的结果转换回一个signed
值,而不在内部更改位;然后这将正确显示-500
,正如您所期望的那样;但是,正如其他海报所指出的那样,这在理论上是相当危险的,因为它在技术上并不能保证工作,尽管它通常会以这种方式与今天的编译器一起工作。
仅使用前两个变量 ( smallValue * value
),您将获得相同的结果。
首先,积分提升适用于两个值:int8_t
变成int
和unsigned int
保持原样。
然后应用 C++11 5/9 中的这条规则来确定结果类型:
否则,如果无符号整数类型的操作数的秩大于或等于另一个操作数类型的秩,则将有符号整数类型的操作数转换为无符号整数类型的操作数的类型。
所以-1
必须转换为unsigned int
使用模运算,给出一个很大的正数。乘以 500 将溢出(再次使用模运算),给出不同的大数。
将'auto'设为签名类型,你会没事的:
long test = smallValue * value * anotherSmallValue;
使用混合类型操作总是容易出现这样的错误。我建议您使用单一类型进行算术运算