0

このプログラムでは、2 つの数値の 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)(最近計算された残差)を比較します。順序が異なることに注意してください。-8(%ebp)と0ではなく、0と-8(%ebp)をtestl比較します。命令は、値(ANDを実行した後)をゼロと比較します。値が正の場合、それはゼロより「大きい」です。このcmpl命令は、2番目のオペランドを1番目のオペランドと比較します。2番目のオペランドが最初のオペランドを超える場合、結果は「より大きい」になります。これは、Intelのマニュアルとアセンブリ言語では、命令が逆の順序でオペランドを使用して記述されているためです。たとえば、3を%eaxに移動すると、「mov%eax、$3」になります。ただし、使用しているアセンブラは、Intelの順序からすべてのオペランドを逆にします(レガシーの理由により)。

したがって、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命令は浮動小数点例外ではなく、除算エラーを生成します。システムがエラーを誤って報告しています。

于 2012-11-11T13:22:42.080 に答える