Я получаю те же результаты, что и вы на моей машине (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
, давая результаты, которые вы видите. 2
в %eax
указывает количество переданных аргументов с плавающей запятой.
Edit:
Вот оптимизированная версия - в этом случае более короткий код может быть легче для понимания. Объяснение то же, что и выше, но с чуть меньшим шумом. Значение с плавающей запятой загружается 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