当前位置: 首页 > news >正文

Printf可变参数使用

参考文档: http://bbs.csdn.net/topics/70288067

(Owed by: 春夜喜雨 http://blog.csdn.net/chunyexiyu 转载请标明来源 )

本文的二个重点:

1. 可变参数实际上通过首个参数的地址来获取其它参数的地址,因为是顺序存储传过来的;

2. 可变参数为了处理方便,所有的浮点型都是按照double型压栈;

因此,像printf采用的可变参数,它只需要使用%f显示double型,不需要管float型。

关注printf的精度,需要先关注printf的实现,关于printf的实现,我们就要关注一下可变参数的实现:

变参的一个例了:

voidtestIntAndFloat(inttype, ...)

{

va_listargptr;

va_start(argptr,type);

if(type== 0 )

{

intn=va_arg(argptr,int);

printf("%d\n",n);

}

elseif(type== 1)

{

doubled=va_arg(argptr,double); // 必须使用double接收,后边会说明原因

printf("%f\n",d);

}

else

{}

}

测试:

floatf1= 12345;

testIntAndFloat(0,123);

testIntAndFloat(1,f1,f1);

输出:

123

12345.0000

查看变参相关的定义:

typedefchar*va_list;

#defineva_start(ap,v) (ap= (va_list)_ADDRESSOF(v) +_INTSIZEOF(v) )

#define_ADDRESSOF(v) ( &reinterpret_cast<constchar&>(v))

#define_INTSIZEOF(n) ( (sizeof(n) +sizeof(int) - 1)& ~(sizeof(int)- 1) )

#defineva_arg(ap,t) ( *(t*)((ap+=_INTSIZEOF(t)) -_INTSIZEOF(t)) )

//_ADDRESSOF(v):获取v的地址,并转为char*型

//_INTSIZEOF(n):实现4字节对齐

//va_start(ap,v): 获取参数v之后的地址

//va_arg(av, t): ap地址向后移t类型size,并返回ap地址之前t类型变量的值

看一个输出%d的例子:

__int64i= 123456;

printf("%d,%d \n",i,i);

结果为: 123456, 0

printf("%d,%d,%d,%d \n",i,i);

结果为: 123456,0,123456,0

printf("%lld,%lld \n",i,i);

结果为: 123456, 123456

从上面的内容可以看出来:

%d时,每次读取的位数是4位,而int64有8位,所以如果使用%d的话,分两次读取完。

(由于是little endian,先读低位,再读高位,先读到123456,再读到0)

再看输出%f的例子

floatf= 123456789123456789.0;

double fd = f;

doubled= 123456789123456789.0;

printf("%f\%f\n%f\n",f,fd,d);

结果为:

1234567939550609400.0000

1234567939550609400.0000

1234567890123456800.0000

可以看出:

float实际是被转成double型存储显示的

(float的精底通常是6-7位,double是15-16位,printf的时候,显示的位数是按double算的。)

再看这个例子,运行时观察内存信息:

(观察内存方式: VC->调试->窗口->内存)

voidtestArgs(inttype, ...)

{

va_listargptr;

va_start(argptr,type);

for(inti=0;i<8;i++)

{

int*pNumber= (int*)argptr;

float*pFloat= (float*)argptr;

va_arg(argptr,int);

printf(("\naddrss:%d int value: %d float value: %f"),pNumber, *pNumber, *pFloat);

}

}

voidTestFuncArgs(intn1= 31,floatf1=31.0,doubled1=31.0,charc1=31,boolb1=31,shorts1=31,__int64n2=31,intn3=31)

{

printf("\nf1=%f",f1);

printf(("\naddrss of arg(int): %x, value: %d"), &n1,n1);

printf(("\naddrss of arg(float): %x, value: %d|%d, float:%f"), &f1,f1,f1);

printf(("\naddrss of arg(double): %x, value: %d|%d, float:%f"), &d1,d1,d1);

printf(("\naddrss of arg(char): %x, value: %d"), &c1,c1);

printf(("\naddrss of arg(bool): %x, value: %d"), &b1,b1);

printf(("\naddrss of arg(short): %x, value: %d"), &s1,s1);

printf(("\naddrss of arg(__int64): %x, value: %d"), &n2,n2);

printf(("\naddrss of arg(int): %x, value: %d"), &n3,n3);

testArgs(n1,f1,d1,c1,b1,s1,n2,n3);

}

当按函数定参参数形式传递TestFuncArgs时:

Int/char/bool/short占用4字节

Float占用4字节

Double点用8字节

__int64占用8字节

内存中连续显示

1f 00 00 00 --int

00 00 f8 41 --float

00 00 00 00 00 00 3f 40 --double

1f 00 00 00 --char

01 00 00 00 --bool

1f 00 00 00 --short

1f 00 00 00 00 00 00 00 –int64

1f 00 00 00 00 00 -- int

当采用不定参数传递testArgs时:

其它均不变,但float为8字节存储,这个是需要非常注意的一个事情。使用%d打印时,只会取4字节,需要使用两个%d%d才能打印一个float。

内存中连续显示

1f00 00 00 -- int

00 00 00 00 00 00 3f 40 --float

00 00 00 00 00 00 3f 40 --double

1f 00 00 00 --char

0100 00 00 --bool

1f 00 00 00 --short

1f 00 00 00 00 00 00 00 –int64

1f 00 00 00--int
(Owed by: 春夜喜雨 http://blog.csdn.net/chunyexiyu 转载请标明来源)
http://www.gsyq.cn/news/1583469.html

相关文章:

  • 《全球芯片图鉴》8 锦锐科技
  • 嵌入式DSP开发进阶:掌握LCF预处理与预定义符号,优化内存与缓存配置
  • OpenClaw:基于CLI与设备直连的AI工作流中枢
  • Selenium与Playwright对照代码版:工程化自动化选型实战指南
  • Flask/Jinja2 SSTI漏洞实战:从原理到RCE利用链完整解析
  • OpenClaw卸载指南:npm CLI工具清理全攻略
  • 麻辣龙虾:OpenClaw一键本地智能体安装包实战指南
  • MATLAB GUI开发实战:从App Designer入门到独立应用部署
  • DeepCodex本地中继:实现Codex与DeepSeek协议兼容的技术方案
  • 多智能体系统中的公平性挑战与解决方案
  • Windows本地部署飞书数字员工:PowerShell一键启用AI自动化
  • OpenCLAW飞书云原生集成:零代码AI能力嵌入工作流
  • Agent Skills:从技能文档到行为契约的工程化实践
  • 密码掩码设计全解析:从安全原理到前端实现的最佳实践
  • Sora内测申请实战指南:从资格获取到高效应用全解析
  • 从实战视角解析学生方程式大赛:线控刹车标定与数据采集系统应用
  • MPC8641D PCIe控制器错误捕获与配置空间访问机制详解
  • 长上下文大模型在金融招股书理解中的实战突破
  • Llama4应用构建:基于DLAI范式的可监控生产流水线
  • GUIDE跨控件数据访问:从原理到实践的MATLAB GUI开发指南
  • 用 Nacos 3.2 构建企业级 Skills Registry
  • MATLAB eigshow 交互式学习:特征值与奇异值分解的几何可视化
  • 科学计算代码现代化重构:从Python 2祖传算法到可维护工程实践
  • 安卓APP逆向实战:从静态分析到动态验证的完整流程解析
  • IoT数据分析实战:从传感器数据到智能决策的完整指南
  • DeepSeek V4 实质是工程成熟度代号:R1模型+协议网关的本地AI开发落地实践
  • Hermes Agent Linux安装指南:轻量级AI智能体运行时部署实战
  • Linux内核堆溢出漏洞CVE-2022-0995深度剖析与复现
  • MATLAB R2019a核心特性解析:性能优化、工作流与深度学习应用
  • ATM控制器地址压缩与ABR流控机制深度解析