printf类型不匹配且参数顺序错误却输出正确值的原因探究
为啥参数顺序和类型都错了,printf还能输出正确结果?
哇,这个现象确实挺反直觉的,我来帮你拆解背后的底层逻辑——本质上这是未定义行为下的巧合,咱们一步步看:
首先先把你的代码贴出来方便分析:
#include <stdio.h> #include <stdint.h> int main() { int64_t qty = 900; double p = 74.45; printf( "%f|%ld\n", p, qty ); printf( "%f|%ld\n", qty, p ); return 0; }
核心原因:可变参数函数的调用约定 + 类型分离存储
printf是一个可变参数函数,它不会检查传入参数的类型、数量是否和格式字符串匹配——它完全按照格式字符串的指示,去特定位置读取对应类型的数据。
以x86_64平台的System V调用约定(大部分Linux/macOS采用这个)为例,参数传递是分“组”的:
- 整数/指针类型的参数,会依次放入通用寄存器
RDI、RSI、RDX...(RDI留给printf的第一个参数,也就是格式字符串) - 浮点类型的参数,会依次放入浮点寄存器
XMM0、XMM1、XMM2...
咱们分别看两次printf的调用细节:
第一次调用:printf("%f|%ld\n", p, qty);
- 格式字符串
"%f|%ld\n"被放入RDI(第一个参数) p是double(浮点类型),放入XMM0(第一个浮点参数位置)qty是int64_t(整数类型),放入RSI(第二个整数参数位置)printf解析格式字符串:- 遇到
%f,就去**第一个浮点寄存器XMM0**读取,拿到p的74.45,正确 - 遇到
%ld,就去**第二个通用寄存器RSI**读取,拿到qty的900,正确
- 遇到
第二次调用:printf("%f|%ld\n", qty, p);
- 格式字符串同样放入
RDI qty是整数类型,放入RSI(第二个整数参数位置)p是浮点类型,放入XMM0(第一个浮点参数位置)printf还是按照格式字符串的要求读取:- 遇到
%f,依旧去**第一个浮点寄存器XMM0**读取,刚好这里放的是p,拿到74.45 - 遇到
%ld,依旧去**第二个通用寄存器RSI**读取,刚好这里放的是qty,拿到900
- 遇到
你看,虽然你把参数顺序写反了,但两种类型的参数是分别存在不同的寄存器组里的,printf根本不管你参数的顺序,只看格式字符串要求的类型,去对应组的对应位置取——这次刚好歪打正着,两个参数在各自的寄存器组里都是第一个,所以输出正确。
重要提醒:这是未定义行为!
千万不要依赖这个现象!未定义行为意味着编译器可以任意处理,换个平台(比如32位x86,参数都存在栈上)、换个编译器版本、甚至只是修改代码的其他部分,结果都可能完全错误(比如输出垃圾值、程序崩溃)。
写printf的时候一定要保证格式字符串和参数的类型、数量严格对应,这才是正确的写法。
内容的提问来源于stack exchange,提问作者ByteMe95
相关产品推荐
相关产品推荐

