我最近在我的C++代码上遇到了一个问题,这使我想知道我是否对编译器长时间操作会产生一些误解...
只需看下面的代码:
#include <iostream>
int main() {
int i = 1024, j = 1024, k = 1024, n = 3;
long long l = 5;
std::cout << i * j * k * n * l << std::endl; // #1
std::cout << ( i * j * k * n ) * l << std::endl; // #2
std::cout << l * i * j * k * n << std::endl; // #3
return 0;
}
对我来说,这3行中的任何一行发生乘法的顺序都是不确定的。但是,这是我认为会发生的情况(假设
int
为32b,long long
为64b,它们都遵循IEEE规则):int
作为中间结果来评估括号,从而导致溢出并存储-1073741824。对于最后的乘法运算,此中间结果被提升为long long
,因此打印结果应为-5368709120。 现在,对于#1和#3行,我不确定:我认为尽管评估顺序未定义,但是编译器会将所有操作“提升”为最大操作数的类型,即
long long
。因此,在这种情况下不会发生溢出,因为所有计算都将在64b中进行...但这是GCC 5.3.0为我提供的以下代码:~/tmp$ g++-5 cast.cc
~/tmp$ ./a.out
-5368709120
-5368709120
16106127360
我也希望第一个结果为16106127360。由于我怀疑GCC中是否存在如此庞大的编译器错误,因此我认为该错误位于键盘和椅子之间。
任何人都可以确认/确认这是未定义的行为,并且GCC正确地给予了我所提供的任何信息(因为这是未定义的)?
最佳答案
GCC是正确的。
例如,第一个表达式被解析为
i * j * k * n * l = ((((i * j) * k) * n) * l)
,并且提升仅在计算了最后一个乘法时发生,但是此时左操作数已经不正确。