为什么gcc对x86上的整数溢出会导致无限循环?

内容来源于 Stack Overflow,并遵循CC BY-SA 3.0许可协议进行翻译与使用

  • 回答 (2)
  • 关注 (0)
  • 查看 (39)

在gcc上,以下代码进入无限循环:

#include <iostream>
using namespace std;

int main(){
    int i = 0x10000000;

    int c = 0;
    do{
        c++;
        i += i;
        cout << i << endl;
    }while (i > 0);

    cout << c << endl;
    return 0;
}

所以我们的协议是:有符号整数溢出在技术上是未定义的行为。但是gcc在x86上使用x86整数指令来实现整数算法,这种指令是在溢出的情况下包装的。

我使用:

~/Desktop$ g++ main.cpp -O2
~/Desktop$ ./a.out
536870912
1073741824
-2147483648
0
0
0

... (infinite loop)

如果禁用优化,则没有无限循环,输出也是正确的。VisualStudio也正确地编译了这一点,并给出了以下结果:

正确输出:

~/Desktop$ g++ main.cpp
~/Desktop$ ./a.out
536870912
1073741824
-2147483648
3

以下是其他一些变化:

i *= 2;   //  Also fails and goes into infinite loop.
i <<= 1;  //  This seems okay. It does not enter infinite loop.

以下是所有相关的版本信息:

~/Desktop$ g++ -v
Using built-in specs.
COLLECT_GCC=g++
COLLECT_LTO_WRAPPER=/usr/lib/x86_64-linux-gnu/gcc/x86_64-linux-gnu/4.5.2/lto-wrapper
Target: x86_64-linux-gnu
Configured with: ..

...

Thread model: posix
gcc version 4.5.2 (Ubuntu/Linaro 4.5.2-8ubuntu4) 
~/Desktop$ 

所以问题是:这是GCC中的一个bug吗?还是我误解了gcc如何处理整数算法?

*我也在标记这个C,因为我假设这个bug会在C中复制。(我还没有核实。)

编辑:

下面是循环的程序集:(如果我正确地识别它)

.L5:
addl    %ebp, %ebp
movl    $_ZSt4cout, %edi
movl    %ebp, %esi
.cfi_offset 3, -40
call    _ZNSolsEi
movq    %rax, %rbx
movq    (%rax), %rax
movq    -24(%rax), %rax
movq    240(%rbx,%rax), %r13
testq   %r13, %r13
je  .L10
cmpb    $0, 56(%r13)
je  .L3
movzbl  67(%r13), %eax
.L4:
movsbl  %al, %esi
movq    %rbx, %rdi
addl    $1, %r12d
call    _ZNSo3putEc
movq    %rax, %rdi
call    _ZNSo5flushEv
cmpl    $3, %r12d
jne .L5
提问于
用户回答回答于

当标准说它是未定义的行为时,我是认真的...。任何事都可能发生。“任何事情”都包括“通常整数环绕,但有时会发生奇怪的事情”。

是的,在x86 CPU上,整数通常按你所期望的方式包装编译器假定您不会导致未定义的行为,并对循环测试进行优化。如果你真的想绕过去-fwrapvg++gcc在编译时,这会给您定义良好的溢出语义(双补足),但会损害性能。

用户回答回答于

它很简单:未定义的行为--特别是使用优化(-O2)打开-意味着什么都行有可能发生。

预期的行为,而不使用-O2切换。

顺便说一下,ICL和TCC都很好,但是你不能依赖这样的东西.

gcc优化实际上利用了有符号整数溢出。这意味着“bug”是由设计造成的。

扫码关注云+社区