0

通过这个程序,我打算找到两个数字的 GCD。但我得到的结果是“浮点异常(核心转储)”。问题是什么?我试图生成的代码是

int main() {
int sml, lrg, rem;
read %d sml
read %d lrg
while (sml > 0){
rem = lrg % sml;
lrg = sml;
sml = rem;
}
print %d lrg;
return 0;
}

我生成的汇编文件是:

    .file "gcd.c"
    .section .rodata
.LC0:
    .string "%d"
.LC1:
    .string "%d\n"

    .text
    .globl main
    .type main, @function
main:
    pushl %ebp
    movl  %esp, %ebp
    andl  $-16, %esp
    subl  $32, %esp

    leal  -8(%ebp), %eax    #scan a value
    movl  %eax,  4(%esp)
    movl  $.LC0,  (%esp)
    call scanf

    leal  -12(%ebp), %eax   #scan a value
    movl  %eax,  4(%esp)
    movl  $.LC0,  (%esp)
    call scanf


.L2:
    movl $0, %eax
    cmpl -8(%ebp),%eax
    jle .L0
    jmp .L1

.L0:
    movl  -12(%ebp),%eax
    movl -8(%ebp),%ecx
    movl %eax,%edx
    sarl $31, %edx
    idivl %ecx
    movl %edx,%eax
    movl %eax, -16(%ebp)
    movl -8(%ebp),%edx
    movl %edx, -12(%ebp)
    movl -16(%ebp),%edx
    movl %edx, -8(%ebp)
    jmp .L2

.L1:
    movl -12(%ebp), %eax
    movl  %eax,  4(%esp)
    movl $.LC0, (%esp)
    call printf

    movl $0, %edx

    movl $0, %eax       #end of program
    leave
    ret

.LFE2:
    .size     main, .-main 
    .ident     "GCC: (GNU) 4.2.3 (4.2.3-6mnb1)" 
    .section    .note.GNU-stack,"",@progbits

另一方面,此汇编代码有效

    .file   "check.c"
    .section    .rodata
.LC0:
    .string "%d"
    .text
    .globl  main
    .type   main, @function
main:
.LFB0:
    .cfi_startproc
    pushl   %ebp
    .cfi_def_cfa_offset 8
    .cfi_offset 5, -8
    movl    %esp, %ebp
    .cfi_def_cfa_register 5
    andl    $-16, %esp
    subl    $32, %esp
    leal    20(%esp), %eax
    movl    %eax, 4(%esp)
    movl    $.LC0, (%esp)
    call    scanf
    leal    24(%esp), %eax
    movl    %eax, 4(%esp)
    movl    $.LC0, (%esp)
    call    scanf
    jmp .L2
.L3:
    movl    24(%esp), %eax
    movl    20(%esp), %ecx
    movl    %eax, %edx
    sarl    $31, %edx
    idivl   %ecx
    movl    %edx, 28(%esp)
    movl    20(%esp), %eax
    movl    %eax, 24(%esp)
    movl    28(%esp), %eax
    movl    %eax, 20(%esp)
.L2:
    movl    20(%esp), %eax
    testl   %eax, %eax
    jg  .L3
    movl    24(%esp), %eax
    movl    %eax, 4(%esp)
    movl    $.LC0, (%esp)
    call    printf
    movl    $0, %eax
    leave
    .cfi_restore 5
    .cfi_def_cfa 4, 4
    ret
    .cfi_endproc
.LFE0:
    .size   main, .-main
    .ident  "GCC: (Ubuntu/Linaro 4.6.3-1ubuntu5) 4.6.3"
    .section    .note.GNU-stack,"",@progbits
4

2 回答 2

3

您可能在此行中除以零:

idivl %ecx

在寄存器中有一个0值。ecx

于 2012-11-10T21:58:11.027 回答
2

你的比较和跳跃是错误的。工作代码有:

testl   %eax, %eax
jg  .L3

损坏的代码有:

movl $0, %eax
cmpl -8(%ebp),%eax
jle .L0
jmp .L1

前者将 %eax(包含最近计算的残差)与零进行比较,如果残差为正(大于零),则继续循环(跳转到 .L3)。

后者将 0 与 -8(%ebp)(最近计算的残差)进行比较。注意顺序不同;它将 0 与 -8(%ebp) 进行比较,而不是将 -8(%ebp) 与 0 进行比较。该testl指令将值(在执行 AND 之后)与零进行比较。如果该值为正,则它“大于”零。该cmpl指令将其第二个操作数与其第一个操作数进行比较;如果第二个操作数超过第一个,结果是“大于”。这是因为,在英特尔的手册和汇编语言中,指令的操作数是以相反的顺序编写的。例如,将 3 移入 %eax 将是“mov %eax, $3”。但是,您使用的汇编程序将所有操作数从 Intel 的顺序中反转(由于遗留原因)。

因此,如果 0 小于或等于余数,则损坏的代码将继续循环(跳转到 .L0)。因此,如果余数为零,则循环继续。您可以将其更改jlejl

jl .L0

或者,您可以消除多余的无条件跳转:

movl $0, %eax
cmpl -8(%ebp),%eax
jge .L1

此外,您可能想要更改:

movl $.LC0, (%esp)
call _printf

到:

movl $.LC1, (%esp)
call _printf

这样您就可以传递"%d\n"给 printf 而不是传递"%d".

顺便说一句,该idivl指令会产生除法错误,而不是浮点异常。您的系统误报了该错误。

于 2012-11-11T13:22:42.080 回答