对于下面的代码我得到溢出但遗憾的是我似乎无法理解为什么。
std::int8_t smallValue = -1;
unsigned int value = 500;
std::uint8_t anotherSmallValue = 1;
auto test = smallValue * value * anotherSmallValue;
之后test
是一个非常大的值。
有人可以解释一下,这里会发生什么?
答案 0 :(得分:6)
结果类型为unsigned int
test here。 smallValue * value
smallValue
将被转换为无符号,因此此表达式为(unsigned)-1 * 500
。但是,如果使用-Wsign-conversion
编译此代码 - 编译器告诉您,您做了坏事。 link
答案 1 :(得分:5)
当编译器看到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
。
只需转回signed
(例如,将值test
定义为int
,而不是auto
,反过来通常会整个操作的结果返回到signed
值,而不是在内部更改位;这将正如您所期望的那样正确显示-500
;但是,正如其他海报所指出的那样,这是相当危险的理论,因为它在技术上并不能保证工作,尽管它通常会以今天的编译器的方式工作。
答案 2 :(得分:2)
只有前两个变量(smallValue * value
)才会得到相同的结果。
首先,对这两个值应用积分促销:int8_t
变为int
,unsigned int
保持不变。
然后应用C ++ 11 5/9中的这个规则来确定结果类型:
否则,如果具有无符号整数类型的操作数的等级大于或等于 另一个操作数的类型的等级,有符号整数类型的操作数应转换为 具有无符号整数类型的操作数的类型。
所以-1
必须使用模运算转换为unsigned int
,给出一个大的正数。乘以500将溢出(再次使用模运算),给出不同的大数。
答案 3 :(得分:1)
将'auto'变为签名类型,你会没事的:
long test = smallValue * value * anotherSmallValue;
答案 4 :(得分:1)
使用混合类型操作总是容易出现此类错误。我建议你使用单一类型进行算术运算