c++ - 为什么使用 GCC 在 x86 上整数溢出会导致无限循环?

标签 c++ c gcc x86 undefined-behavior

以下代码在 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;
}

所以这里是交易:有符号整数溢出在技术上是未定义的行为。但是 x86 上的 GCC 使用 x86 整数指令实现整数运算 - 溢出时换行。

因此,我原以为它会在溢出时换行——尽管它是未定义的行为。但显然情况并非如此。那么我错过了什么?

我使用以下代码编译:

~/Desktop$ g++ main.cpp -O2

GCC 输出:

~/Desktop$ ./a.out
536870912
1073741824
-2147483648
0
0
0

... (infinite loop)

禁用优化后,没有无限循环,输出正确。 Visual Studio 也可以正确编译它并给出以下结果:

正确的输出:

~/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 中的错误吗?还是我误解了 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 上,整数通常按照您期望的方式包装。 This is one of those exceptions.编译器假定您不会导致未定义的行为,并优化循环测试。如果你真的想要环绕,编译时将 -fwrapv 传递给 g++gcc ;这为您提供了明确定义的(二进制补码)溢出语义,但会损害性能。

关于c++ - 为什么使用 GCC 在 x86 上整数溢出会导致无限循环?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/7682477/

相关文章:

android - 是否可以在 C++ 中创建控件并在 Java Activity 中使用?

c++ - 在 main.cpp 问题中从单独的 C++ 文件调用函数

c++ - 在 C++ 中使用 TCP 从服务器端向客户端发送文件

c++ - 在客户端等待服务器的响应

c++ - 获得对齐内存的最佳跨平台方法

c - 警告...已定义但未使用 - 我真的需要 .c 文件吗?

创建游戏 Rush Hour,但错误检查器出现问题。 (C)

c - 迷你 shell 项目中的信号处理程序

C 静态库无法与 librt 链接

c - 指向 C 中的结构 - 错误 : expected ')' before '*' token