您的位置:首页 > 其它

[置顶] 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的实现,我们就要关注一下可变参数的实现:

变参的一个例了:

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 转载请标明来源
 
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: