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++ 编译它会给出随机结果而不是第二个 0。

我知道我给了无效的说明符printf并且它触发了未指定未定义的行为,但我想知道为什么会发生这种特定的损坏,因为long long它们double的大小相同。

4

3 回答 3

10

这是因为根据x86_64您平台上的 C 调用约定,前两个浮点参数在xmm0and中传递xmm1,前两个整数参数在 GPR 中传递(rsi如果rdx您在 Linux 或 OS X 上),无论顺序如何他们出现在其中。

你很困惑,因为你期望参数是在内存中传递的;他们不是。

于 2010-09-08T17:30:00.927 回答
5

我得到的结果与您在我的机器上所做的相同(Mac OS X,所以 AMD/Linux ABI)。浮点参数在 XMM 寄存器中传递,整数参数在整数寄存器中传递。当printf使用 抓取它们va_arg时,它会在看到%f格式时从 XMM 中提取,而在看到 时从其他寄存器中提取%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
  • 第一个数字应该是一个高值,因为您将双精度数作为整数传递。它应该是 %f。
  • “resi”中“0”之后的句点是什么?这会将它变成一个双精度数,因此您尝试将双精度数加载到一个整数中。那应该给你一个编译器警告。
  • 某些实现可能是基于寄存器的,因此由于您弄乱了参数类型,因此会感到困惑。

你在什么平台上编译?视窗?

您是否查看了反汇编以查看它实际推送到堆栈上的内容?它甚至将它们推入堆栈还是使用寄存器?

于 2010-09-08T17:31:32.270 回答