我最近在我的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规则):
  • 对于第2行,首先使用int作为中间结果来评估括号,从而导致溢出并存储-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
    

    我也希望第一个结果为16106127360。由于我怀疑GCC中是否存在如此庞大的编译器错误,因此我认为该错误位于键盘和椅子之间。

    任何人都可以确认/确认这是未定义的行为,并且GCC正确地给予了我所提供的任何信息(因为这是未定义的)?

    最佳答案

    GCC是正确的。

  • 乘法的关联性是left to right。这意味着所有这些表达式都是从左到右求值的。
  • 升级到更高类型的代码仅在不同类型的单个二进制运算符的两个操作数之间。

  • 例如,第一个表达式被解析为i * j * k * n * l = ((((i * j) * k) * n) * l),并且提升仅在计算了最后一个乘法时发生,但是此时左操作数已经不正确。

    10-04 22:27
    查看更多