printf:如何解释损坏的结果

Art*_*cto 1 c linux 64-bit printf calling-convention

#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;
}
Run Code Online (Sandbox Code Playgroud)

给出(Linux,gcc,x64)

0 0.000116 0 0.000116
             ^^^^^^^^ odd, since the memory for resi is zeroed

实际上,用g ++编译它会得到随机结果,而不是第二个0.

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

Ste*_*non 10

这是因为在x86_64C调用你的平台上约定,前两个浮点参数在传递xmm0xmm1,和前两个整型参数在GPRS传送(rsirdx如果你在Linux或OS X),不分先后顺序的它们出现在哪里.

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


Car*_*rum 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
Run Code Online (Sandbox Code Playgroud)

在那里,你可以看到这是怎么回事-格式字符串传递的%rdi,那么你的参数传递(按顺序)中: %xmm0,%xmm1,%rsi,和%rdx.如果printf 赢了钱,它会弹出发挥他们在不同的顺序(在你的格式字符串指定的顺序).这意味着它会弹出他们: %rsi,%xmm0,%rdx,%xmm1,让您看到的结果.该2%eax是指示传递浮点参数的个数.

编辑:

这是一个优化版本 - 在这种情况下,较短的代码可能更容易理解.解释与上面相同,但是样板噪声稍微小一些.浮点值由movsd在线4 加载.

 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
Run Code Online (Sandbox Code Playgroud)