我在 OSX 上的 gcc 中不断得到 "Segmentation fault: 11",但在 Windows 上工作

I keep getting "Segmentation fault: 11" in gcc on OSX, but works on Windows

该程序编译正常,但当我为 mult(x,y) 的第一个参数输入负数时无法执行。它显示消息 "Segmentation fault: 11"。我在 Windows 上试过了,效果很好。

在X代码中编译时的错误信息是:Thread 1: EXC_BAD_ACCESS (code=2, address=0x7fff5f3ffffc)

为什么这在 OSX (El Capitan) 上不起作用但在 Windows 上没问题?非常感谢!

#include <stdio.h>

signed succ(signed a)
{
    return a+1;
}

signed pre(signed b)
{
    return b-1;
}

signed add(signed c, signed d)
{

    if(d == 0) return c;

    return succ(add(c, pre(d)));

}


signed sub(signed e, signed f)
{
    if(f == 0) return e;

    return pre(sub(e, pre(f)));
}



signed mult(signed g, signed h)
{
    if(h == 0 || g == 0) return 0;

    return add(g,mult(g,pre(h)));
}


int main()
{


    printf("mult -2,2: %i\n", mult(-2,2));


    return 0;
}

由于堆栈溢出而崩溃,multadd 函数被递归调用。

虽然最好使用调试器,但在每个函数中添加一个printf可以帮助观察程序流程。例如将 mult 更改为:

signed mult(signed g, signed h)
{
    printf(" mult %d %d\n", g, h);
    if(h == 0 || g == 0) return 0;

    return add(g,mult(g,pre(h)));
}

和其他函数类似...输出是:

mult -2 2
pre 2
mult -2 1
pre 1
mult -2 0
add -2 0
add -2 -2
pre -2
add -2 -3
pre -3
add -2 -4
pre -4
add -2 -5
pre -5
add -2 -6
pre -6
add -2 -7
pre -7
add -2 -8
pre -8
add -2 -9
pre -9
add -2 -10
...

并且它继续调用 add,第二个参数递减,直到它崩溃。 add 中的条件 if(d == 0) 仅当该参数溢出时才会变为 true(在 sizeof signed 为 4,即 32 位的情况下,将在超过 40 亿次迭代后发生).

当它工作时,我唯一能想到的就是编译器进行优化并可能删除函数调用。 在我的 Windows x64 机器上尝试使用来自 MinGW 的 gcc 构建的原始程序:

  • 构建时崩溃:gcc file.c

  • gcc -O3 file.c构建成功运行(这里我改变了优化级别)

在使用 -O3 构建的可执行文件上,我 运行 objdump -S a.exe > a.lst 这是反汇编的 mult 函数:

0000000000401550 <mult>:
  401550:   53                      push   %rbx
  401551:   48 83 ec 20             sub    [=12=]x20,%rsp
  401555:   31 c0                   xor    %eax,%eax
  401557:   85 d2                   test   %edx,%edx
  401559:   89 cb                   mov    %ecx,%ebx
  40155b:   75 06                   jne    401563 <mult+0x13>
  40155d:   48 83 c4 20             add    [=12=]x20,%rsp
  401561:   5b                      pop    %rbx
  401562:   c3                      retq   
  401563:   85 c9                   test   %ecx,%ecx
  401565:   74 f6                   je     40155d <mult+0xd>
  401567:   83 fa 01                cmp    [=12=]x1,%edx
  40156a:   75 08                   jne    401574 <mult+0x24>
  40156c:   01 d8                   add    %ebx,%eax
  40156e:   48 83 c4 20             add    [=12=]x20,%rsp
  401572:   5b                      pop    %rbx
  401573:   c3                      retq   
  401574:   83 fa 02                cmp    [=12=]x2,%edx
  401577:   89 c8                   mov    %ecx,%eax
  401579:   74 f1                   je     40156c <mult+0x1c>
  40157b:   83 ea 03                sub    [=12=]x3,%edx
  40157e:   e8 cd ff ff ff          callq  401550 <mult>
  401583:   85 c0                   test   %eax,%eax
  401585:   74 0b                   je     401592 <mult+0x42>
  401587:   01 d8                   add    %ebx,%eax
  401589:   74 0d                   je     401598 <mult+0x48>
  40158b:   01 d8                   add    %ebx,%eax
  40158d:   01 d8                   add    %ebx,%eax
  40158f:   90                      nop
  401590:   eb dc                   jmp    40156e <mult+0x1e>
  401592:   89 d8                   mov    %ebx,%eax
  401594:   01 d8                   add    %ebx,%eax
  401596:   eb f5                   jmp    40158d <mult+0x3d>
  401598:   89 d8                   mov    %ebx,%eax
  40159a:   01 d8                   add    %ebx,%eax
  40159c:   eb d0                   jmp    40156e <mult+0x1e>
  40159e:   90                      nop
  40159f:   90                      nop

可以看出,它没有调用任何其他函数,只调用它自己 (callq 401550 <mult>)。所以编译器优化/内联了所有其他调用。并通过添加 printf (但仅在此函数中,不破坏优化),输出为:

 mult -2 2
 mult -2 1
 mult -2 0
mult -2,2: -4

看起来好像优化器中有一个盲点。我认为应该崩溃的代码。查看优化后的代码会很有趣。我看不出什么会导致优化器混淆。也许优化器没有考虑可能发生的符号翻转。所以它假设一个负数减一将永远是一个负数,例如...