通过这个程序,我打算找到两个数字的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
答案 0 :(得分:3)
你可能在这一行中除以零:
idivl %ecx
0
注册中的ecx
值。
答案 1 :(得分: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”。但是,您使用的汇编程序会撤消英特尔订单中的所有操作数(由于遗留原因)。
因此,如果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
指令会产生除法错误,而不是浮点异常。您的系统误报了错误。