c - printf() 在 x86-64 平台上给出相同的输出,即使在交换参数时也是如此

标签 c printf variadic-functions

考虑以下代码:

#include <stdio.h>

int main() {
    printf("%lf %ld\n", 1234.0, 5678L);
    printf("%lf %ld\n", 5678L, 1234.0);
}

printf 的两次调用都打印相同的文本 1234.000000 5678,这与第二次调用的代码不太匹配(可能应该是 5678.0000 1234).

我在 x86-64 处理器上的 Linux 4.x 上,但我无法在 x86(32 位)上重现它。我想它可以在任何基于 amd64 架构的 Linux 系统上重现。

为什么交换参数为 printf 提供相同的输出,为什么它特定于 x86-64?

最佳答案

答案是因为它是 System V ABI x86-64 定义参数传递方式的方式。

根据 PDF第 22 页,前 6 个整数参数在 %rdi、%rsi、%rdx、%rcx、%r8、%r9 上传递,前 8 个浮点参数从 %xmm0 传递到 %xmm7。但是,整数和 float 之间没有特定的顺序。因此,尽管定义不同,但以下两个函数的行为相同。

int f1(int i1, int i2, int i3, double d1, double d2, double d3);
int f2(double d1, double d2, int i1, int i2, double d3, int i3);

按照 Syetem V x86-64 ABI 编译,两个函数都将在寄存器 %rdi、%rsi 和 %rdx 中接收 i1、i2 和 i3,在寄存器 %xmm0、%xmm1、%xmm2 中接收 d1、d2 和 d3 .

可变参数也不异常(exception)。最多 6 个整数和最多 8 个 float 通过寄存器传递,其余在堆栈上传递。

说到这段具体的代码,通过查看gcc -O0 -S生成的汇编代码,我验证了上面的说法:整数5678是通过%rsi发送给printf的,(double -precision) 浮点值 1234.0 通过 %xmm0 发送到 printf。在这两种情况下,%eax 都设置为 1,表示有一个可用的浮点参数。

哦,是的,%rdi 在哪里?实际上,格式化字符串是第一个参数,因此指向该字符串的指针通过 %rdi 传递。

printf 不知道整数是在 float 之前还是相反,它只知道它有一个整数参数(在格式化字符串之后)和一个浮点参数(读取 %等)。这正是两条线产生相同输出的原因。

TODO:有人在这里放了 Godbolt 链接吗?

关于c - printf() 在 x86-64 平台上给出相同的输出,即使在交换参数时也是如此,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/55662415/

相关文章:

c - 如何将包含多个变量的字符串分配给char数组?

C科学记数法指数格式

c - 如何使函数使用不确定数量的参数和va_list组合参数?

c++ - 关于linux中的fork系统调用

c - 使用 "\n"打印二维结构数组时出现问题

c - Free 不会释放作为 va_arg 列表发送的指针

java - 当数组中的参数时调用 Method.invoke()

Clock() 没有按预期工作;避免IO

c - C 多线程程序中的错误

c - lextestpass.l :384: error: expected expression before ‘int’