这是未定义的行为。C 语言标准规定,如果可变参数不具有格式字符串所隐含的类型,那么这就是 UB。在您的第三个打印语句中,您传递了 a double
,但它期待 a uint64_t
。既然是UB,什么事情都有可能发生。
该规范允许实现执行诸如在堆栈上传递整数但通过 FPU 寄存器传递浮点值之类的事情,这就是我怀疑在您的测试用例中发生的事情。例如,Linux on x86 (GCC) 上的cdecl 调用约定在 x87 伪堆栈(寄存器)上传递浮点函数参数ST0...ST7
。
如果您查看生成的程序集,您可能会发现为什么您的第三个和第四个打印语句的行为不同。在带有 Clang 4.1 的 Mac OS X 10.8.2 64 位上,我能够重现类似的结果,并且程序集看起来像这样,我已经注释了:
.section __TEXT,__text,regular,pure_instructions
.globl _test_double
.align 4, 0x90
_test_double: ## @test_double
.cfi_startproc
## BB#0:
pushq %rbp
Ltmp3:
.cfi_def_cfa_offset 16
Ltmp4:
.cfi_offset %rbp, -16
movq %rsp, %rbp
Ltmp5:
.cfi_def_cfa_register %rbp
pushq %rbx
pushq %rax
Ltmp6:
.cfi_offset %rbx, -24
# printf("%lf", double)
movq %rdi, %rbx
movsd (%rbx), %xmm0
leaq L_.str(%rip), %rdi
movb $1, %al
callq _printf
# printf("%lf", uint64_t)
movq (%rbx), %rsi
leaq L_.str1(%rip), %rdi
xorb %al, %al
callq _printf
# printf("%llx", double)
leaq L_.str2(%rip), %rdi
movsd (%rbx), %xmm0
movb $1, %al
callq _printf
# printf("%llx", uint64_t)
leaq L_.str3(%rip), %rdi
movq (%rbx), %rsi
xorb %al, %al
addq $8, %rsp
popq %rbx
popq %rbp
jmp _printf ## TAILCALL
.cfi_endproc
在打印double
值的情况下,它将参数放入SIMD%xmm0
寄存器:
movsd (%rbx), %xmm0
但是对于一个uint64_t
值,它通过整数寄存器传递参数%rsi
:
movq (%rbx), %rsi