c - 即使在交换参数时,printf() 在 x86-64 平台上也能提供相同的输出
问题描述
考虑以下代码:
#include <stdio.h>
int main() {
printf("%lf %ld\n", 1234.0, 5678L);
printf("%lf %ld\n", 5678L, 1234.0);
}
两次调用都printf
打印相同的 text 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。但是,整数和浮点数之间没有特定的顺序。因此,以下两个函数尽管定义不同,但行为相同。
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。
可变参数也不例外。最多 6 个整数和最多 8 个浮点数通过寄存器传递,其余的通过堆栈传递。
说到这个具体的代码,通过查看生成的汇编代码gcc -O0 -S
,我验证了上面的语句:整数5678通过%rsi发送给printf,(双精度)浮点值1234.0通过%xmm0发送给printf . 在这两种情况下,%eax 都设置为 1,表示有一个浮点参数可用。
哦,是的,%rdi 在哪里?实际上,格式化字符串是第一个参数,所以指向字符串的指针通过 %rdi 传递。
printf 不知道整数是在浮点数之前还是其他方式,它只知道它有一个整数参数(在格式化字符串之后)和一个浮点参数(读取 %al)。这正是两条线产生相同输出的原因。
TODO:有人在这里放了一个Godbolt链接?
推荐阅读
- vue.js - 能够使用 bootstrap-vue 在模态下单击内容
- prolog - 如何在没有静态路径的情况下在 GUI 上查看图像
- vue.js - Webpack resolve.alias 中断了以 @ 为前缀的库的导入
- laravel - Laravel 和 Vue - 管理页面和公共页面的独立资产
- chronicle-map - ClassCastException 使用 ByteBuffer 作为 ChronicleMap 的键
- jquery - 表达式语言代码显示为纯文本 JSP
- function - return_of_invalid_type 返回类型“String”不是“void”,由方法“getData”定义
- python - 即使在更新数据库后,SQLAlchemy 也不会在我的表中找到该列?
- angular - 由于 Firebase 的 IDBIndex 错误,无法提供服务器呈现的应用程序
- java - 如何将 Swing 小部件插入 SWT 我在使用 SWT_AWT.new_Frame 时在线程“main”java.lang.IllegalArgumentException 中出现异常