我有一个非常基本的问题,但我不确定我是否理解这个概念。假设我们有:
int a = 1000000;
int b = 1000000;
long long c = a * b;
当我运行时,c
显示负值,因此我也将a
和b
更改为long long
,然后一切都很好。那么,为什么我必须更改a
和b
,当它们的值在int
范围内且其产品已分配到c
时(long long
)?
我正在使用C / C ++
答案 0 :(得分:19)
int
在乘法之前不会被提升为long long
,它们仍然是int
s和产品。然后将产品投射到long long
,但为时已晚,溢出已经发生。
拥有a
或b
long long
之一也应该有效,因为另一个会得到提升。
答案 1 :(得分:3)
对于算术运算符,结果的类型不依赖于您为结果分配的内容,而是依赖于操作数的类型。对于算术运算符,usual arithmetic conversions在操作数上执行。这用于将操作数转换为通用类型,这意味着对于小于 unsigned / signed int的类型,如果值适合,则将它们提升为 unsigned < / em> / 签名 int,在这种情况下,它们已经是int,因此不需要转换。有关原因的详细信息,请参阅Why must a short be converted to an int before arithmetic operations in C and C++?。
我们现在拥有的是未定义的行为,因为有符号整数溢出是未定义的行为,这在草案C ++标准部分5
[Expr] 中有所说明:
如果在评估表达式期间,结果未在数学上定义或不在范围内 其类型的可表示值,行为未定义。 [注意:大多数现有的C ++实现 忽略整数溢出。除以零的处理,使用零除数形成余数,以及所有 浮点异常因机器而异,通常可通过库函数进行调整。 - 后注]
现在我们有清理器来捕获这些类型的未定义行为,并且使用-fsanitize=undefined
同时使用clang和gcc将在运行时捕获此错误( see it live ):
运行时错误:有符号整数溢出:1000000 * 1000000不能 用类型&#39; int&#39;
表示
参考部分5.6
[expr.mul] 说:
[...]通常的算术转换是在操作数上执行的 并确定结果的类型。
和第5
部分说:
否则,应对两个操作数执行整数提升(4.5).61然后执行以下操作 规则应适用于推广的操作数
- 如果两个操作数具有相同的类型,则无需进一步转换。
答案 2 :(得分:0)
这有点荒谬,因为汇编指令总是计算
int * int - &gt; 64位长
所以,如果您查看机器代码,您会看到: 的 IMUL 强> 将64位存储到eax edx中 然后 的 CDQ 强> 将eax的位符号放入edx(从而失去完整的64位结果) 然后将eax edx存储到64位变量
中如果在乘法之前将32位值转换为64位,则无条件地调用64位乘法函数
(我检查过:代码优化时并非如此)
答案 3 :(得分:-1)
指针乘法
定义一个乘法函数,它将两个指针变量作为参数,然后返回指针。从主调用这个函数
#include<iostream>
using namespace std;
int *multiply(int *x,int* y)
{
int mul;
mul = (*x)*(*y);
int *c;
c = &mul;
return c;
}
int main()
{
int x,y;
x = 4;
y = 5;
int *result;
result = multiply(&x,&y);
cout<<*result<<endl;
return 0;
}