1
#include <stdio.h>

int main(void)
{
        double resd = 0.000116;
        long long resi = 0;

        printf("%lld %f %lld %f\n", resd, resd, resi, resi);
        return 0;
}

与えます (Linux、gcc、x64)

0 0.000116 0 0.000116
             ^^^^^^^^ 奇妙です。resi のメモリがゼロになっているためです。

実際、g++ でコンパイルすると、2 番目の 0 の代わりにランダムな結果が得られます。

に無効な指定子を指定したことprintf、およびそれがトリガーされることを理解しています不特定long long未定義の動作ですが、とdoubleが同じサイズであるため、なぜこの特定の破損が発生するのか疑問に思います。

4

3 に答える 3

10

これはx86_64、お使いのプラットフォームの C 呼び出し規約では、最初の 2 つの浮動小数点引数がxmm0andで渡されxmm1、最初の 2 つの整数引数が GPR (rsiおよびrdxLinux または OS X の場合) で渡されるためです (Linux または OS X を使用している場合)。それらが表示されます。

パラメータがメモリに渡されることを期待しているため、混乱しています。そうではありません。

于 2010-09-08T17:30:00.927 に答える
5

私のマシン (Mac OS X、つまり AMD/Linux ABI) と同じ結果が得られます。浮動小数点パラメーターは XMM レジスターで渡され、整数パラメーターは整数レジスターで渡されます。printfを使用してそれらを取得するとva_arg、フォーマットを確認すると XMM から取得し、 を確認%fすると他のレジスタから取得します%lld-O0私のマシンでコンパイルされた( )プログラムの逆アセンブリは次のとおりです。

 1 _main:
 2   pushq   %rbp
 3   movq    %rsp,%rbp
 4   subq    $0x20,%rsp
 5   movq    $0x3f1e68a0d349be90,%rax
 6   move    %rax,0xf8(%rbp)
 7   movq    $0x00000000,0xf0(%rbp)
 8   movq    0xf0(%rbp),%rdx
 9   movq    0xf0(%rbp),%rsi
10   movsd   0xf8(%rbp),%xmm0
11   movq    0xf8(%rbp),%rax
12   movapd  %xmm0,%xmm1
13   movq    %rax,0xe8(%rbp)
14   movsd   0xe8(%rbp),%xmm0
15   lea     0x0000001d(%rip),%rdi
16   movl    $0x00000002,%eax
17   callq   0x100000f22    ; symbol stub for: _printf
18   movl    $0x00000000,%eax
19   leave
20   ret

そこで何が起こっているかを見ることができます - フォーマット文字列が に渡され%rdi、次にパラメータが (順番に) 、 %xmm0%xmm1%rsiに渡されます%rdx。それらを取得するprintf と、別の順序 (フォーマット文字列で指定された順序) でポップオフされます。つまり、それらをポップすることを意味します: %rsi%xmm0%rdx%xmm1、表示される結果を提供します。2inは、%eax渡された浮動小数点引数の数を示します。

編集:

これは最適化されたバージョンです。この場合、短いコードの方が理解しやすいかもしれません。説明は上記と同じですが、ボイラープレート ノイズが少し少なくなっています。浮動小数点値はmovsd4 行目でロードされます。

 1 _main:
 2    pushq   %rbp
 3    movq    %rsp,%rbp
 4    movsd   0x00000038(%rip),%xmm0
 5    xorl    %edx,%edx
 6    xorl    %esi,%esi
 7    movaps  %xmm0,%xmm1
 8    leaq    0x00000018(%rip),%rdi
 9    movb    $0x02,%al
10    callq   0x100000f18   ; symbol stub for: _printf
11    xorl    %eax,%eax
12    leave
13    ret
于 2010-09-08T17:29:20.230 に答える
2
  • doubleを整数として渡すため、最初の数値は高い値にする必要があります。%fである必要があります。
  • 「resi」の「0」以降のピリオドはどうなっていますか?それはそれをdoubleに変えるので、doubleを整数にロードしようとしています。それはあなたにコンパイラ警告を与えるはずです。
  • 一部の実装はレジスタベースである可能性があるため、引数の型をめちゃくちゃにしているため、混乱します。

どのプラットフォームでコンパイルしていますか?ウィンドウズ?

分解を見て、実際にスタックに何がプッシュされるかを確認しましたか?それらをスタックにプッシュするのでしょうか、それともレジスターを使用するのでしょうか?

于 2010-09-08T17:31:32.270 に答える