有了这个程序,我打算找到两个数字的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

最佳答案

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

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”。但是,您使用的汇编程序会将所有操作数从英特尔的顺序反转(由于传统原因)。
因此,如果0小于或等于余数,则断开的代码将继续循环(跳到.L0)。因此,如果余数为零,则循环继续您可以将jle更改为jl
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指令生成除法错误,而不是浮点异常。你的系统误报了错误。

07-24 09:45
查看更多