代码之家  ›  专栏  ›  技术社区  ›  TinyTheBrontosaurus

优化clang处理溢出

  •  3
  • TinyTheBrontosaurus  · 技术社区  · 5 年前

    我的优化器没有预料到我会有一些奇怪的行为。基本上,这是一个变量溢出的情况,我试图用来处理的逻辑被破坏了。

    这是完整的程序( weird.cpp ):

    #include <stdio.h>
    
    class Example {
    public:
      void Change() {
        change_count_++;
    
        // Check for overflow. If it does, set to 1 which is still valid
        if(change_count_ <= 0) {
          change_count_ = 1;
        }
      }
    
    public:
      int change_count_ = 0;
    };
    
    int main() {
      Example example;
      printf("Pre: %d\n", example.change_count_);
    
      example.change_count_ = 2147483647; // MAX_INT
      printf("Mid: %d\n", example.change_count_);
    
      example.Change();
      printf("Pst: %d\n", example.change_count_);
    
      return 0;
    }
    

    使用以下命令生成时:

    gcc  -fPIC -g3 -O1 -g   -std=gnu++11  weird.cpp -o optimized
    gcc weird.cpp -o normal
    

    程序 normal 按预期执行,输出如下:

    Pre: 0
    Mid: 2147483647
    Pst: 1
    

    但是 optimized 给出以下意外行为:

    Pre: 0
    Mid: 2147483647
    Pst: -2147483648
    

    附加到调试程序后,我看到增量是在函数的最后完成的。C++中的溢出行为是未定义的吗?或者我应该用不同的方式来处理这个问题?

    以下是我使用的Clang版本:

    tiny.local:~/scratch/weird_inc$ gcc -v
    Configured with: --prefix=/Library/Developer/CommandLineTools/usr --with-gxx-include-dir=/usr/include/c++/4.2.1
    Apple LLVM version 10.0.0 (clang-1000.10.44.4)
    Target: x86_64-apple-darwin17.7.0
    Thread model: posix
    InstalledDir: /Library/Developer/CommandLineTools/usr/bin
    

    我用 gcc 5.4.0 在Ubuntu,它给了我正确的答案。

    1 回复  |  直到 5 年前
        1
  •  4
  •   Sam Varshavchik    5 年前

    有符号整数溢出 is undefined behavior in the current C++ standard .

    编译器可以自由地假定未定义的行为不会发生,并且不需要编译依赖于未定义行为的代码。

    这意味着编译器可以安全地假定,增加大于0的正值不会导致负值或0。

    您的编译器正在积极地优化代码。编译器看到一个变量被初始化为一个正值,并递增。因此,编译器假定结果不能是负数或0,因此甚至不会编译比较,因为它不可能是真的。