[置顶] Printf可变参数使用
2014-11-15 20:31
113 查看
参考文档: http://bbs.csdn.net/topics/70288067
Owed by: 春夜喜雨 http://blog.csdn.net/chunyexiyu 转载请标明来源
本文的二个重点:
1. 可变参数实际上通过首个参数的地址来获取其它参数的地址,因为是顺序存储传过来的
1. 可变参数为了处理方便,所有的浮点型都是按照double型压栈。
因此,像printf采用的可变参数,它直接使用%f显示double型,不需要管float型。
关注printf的精度,需要先关注printf的实现,关于printf的实现,我们就要关注一下可变参数的实现:
变参的一个例了:
查看变参相关的定义:
typedef
char * va_list;
#define
va_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) )
#define
va_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的例子:
从上面的内容可以看出来:
%d时,每次读取的位数是4位,而int64有8位,所以如果使用%d的话,分两次读取完。
(由于是little endian,先读低位,再读高位,先读到123456,再读到0)
再看输出%f的例子
可以看出:
float实际是被转成double型存储显示的
(float的精底通常是6-7位,double是15-16位,printf的时候,显示的位数是按double算的。)
再看这个例子,运行时观察内存信息:
(观察内存方式: VC->调试->窗口->内存)
当按函数定参参数形式传递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
01
00 00 00 --bool
1f 00 00 00 --short
1f 00 00 00 00 00 00 00
–int64
1f 00 00 00--double
Owed by: 春夜喜雨 http://blog.csdn.net/chunyexiyu 转载请标明来源
Owed by: 春夜喜雨 http://blog.csdn.net/chunyexiyu 转载请标明来源
本文的二个重点:
1. 可变参数实际上通过首个参数的地址来获取其它参数的地址,因为是顺序存储传过来的
1. 可变参数为了处理方便,所有的浮点型都是按照double型压栈。
因此,像printf采用的可变参数,它直接使用%f显示double型,不需要管float型。
关注printf的精度,需要先关注printf的实现,关于printf的实现,我们就要关注一下可变参数的实现:
变参的一个例了:
void testIntAndFloat(int type, ...) { va_list argptr; va_start(argptr, type); if (type == 0 ) { int n = va_arg( argptr, int ); printf( "%d\n", n ); } else if (type == 1) { double d = va_arg( argptr, double); // 必须使用double接收,后边会说明原因 printf( "%f\n", d); } else {} } 测试: float f1 = 12345; testIntAndFloat(0, 123); testIntAndFloat(1, f1, f1); 输出: 123 12345.0000 |
typedef
char * va_list;
#define
va_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) )
#define
va_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的例子:
__int64 i = 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的例子
float f = 123456789123456789.0; double fd = f; double d = 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->调试->窗口->内存)
void testArgs(int type, ...) { va_list argptr; va_start(argptr, type); for (int i=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); } } void TestFuncArgs(int n1 = 31, float f1=31.0, double d1=31.0, char c1=31, bool b1=31, short s1=31, __int64 n2=31, int n3=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
01
00 00 00 --bool
1f 00 00 00 --short
1f 00 00 00 00 00 00 00
–int64
1f 00 00 00--double
Owed by: 春夜喜雨 http://blog.csdn.net/chunyexiyu 转载请标明来源
相关文章推荐
- 使用未公开关键字在 C# 中导入外部 printf 等参数数量可变函数 [1] CLR 支持
- 使用未公开关键字在 C# 中导入外部 printf 等参数数量可变函数 [2] C# 实现
- 可变参数的使用-printf简单实现
- 使用未公开关键字在 C# 中导入外部 printf 等参数数量可变函数 [2] C# 实现
- C语言如何在可变参数函数中使用printf?
- 可变参数函数printf的实现(不使用va_list等内置宏定义)
- 使用未公开关键字在 C# 中导入外部 printf 等参数数量可变函数 [1] CLR 支持
- 使用未公开关键字在 C# 中导入外部 printf 等参数数量可变函数
- 从printf谈可变参数函数的实现
- printf谈可变参数函数的实现
- .NET 指南:使用可变数量的参数的成员
- C语言深入浅出可变参数函数的使用技巧(转)
- 对C的printf函数的可变长参数实现的分析
- 可变参数函数的使用(转载)
- Matlab中使用varargin来实现参数可变的函数
- 编写适合自己需要的printf()函数 -- 可变参数
- 从printf谈可变参数函数的实现--------作者:戎亚新
- 从printf谈可变参数函数的实现
- C语言中可变参数的使用
- 可变参数的使用(C++和C#实现)