整数溢出和操作顺序

Gil*_*les 4 c++ operators multiplication associativity language-lawyer

我最近遇到了一个关于我的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;
}
Run Code Online (Sandbox Code Playgroud)

对我来说,在这3行中的任何一行中发生乘法的顺序是不确定的.但是,这是我认为会发生的事情(假设int是32b,long long是64b,它们都遵循IEEE规则):

  • 对于第2行,首先计算括号,使用ints作为中间结果,导致溢出并存储-1073741824.该中间结果被提升为long long最后一次乘法,因此打印结果应为-5368709120.
  • 由于评估顺序未定义,因此第1行和第3行是"等效的".

现在,对于第1行和第3行,我不确定:我认为虽然评估顺序未定义,但编译器会将所有操作"提升"到最大操作数的类型,即long long此处.因此,在这种情况下不会发生溢出,因为所有的计算都是在64b中进行的......但这就是GCC 5.3.0给我的代码:

~/tmp$ g++-5 cast.cc
~/tmp$ ./a.out 
-5368709120
-5368709120
16106127360
Run Code Online (Sandbox Code Playgroud)

我也期望第一个结果是16106127360.由于我怀疑在GCC中存在如此大规模的编译器错误,我猜错误在于键盘和主席之间.

任何人都可以确认/确认这是未定义的行为,GCC是正确的给我任何给出的(因为这是未定义的)?

gud*_*dok 7

GCC是正确的.

  1. 乘法的相关性是从左到右.这意味着从左到右评估所有这些表达式.
  2. 升级到更高类型只是在不同类型的单个二元运算符的两个操作数之间.

例如,第一个表达式被解析为,i * j * k * n * l = ((((i * j) * k) * n) * l)并且只有在计算了最后一个乘法时才会进行提升,但此时左操作数已经不正确.