Chinaunix首页 | 论坛 | 博客
  • 博客访问: 348889
  • 博文数量: 88
  • 博客积分: 1695
  • 博客等级: 上尉
  • 技术积分: 1380
  • 用 户 组: 普通用户
  • 注册时间: 2009-08-06 15:48
个人简介

喜欢美食, 旅行..

文章分类

全部博文(88)

文章存档

2014年(2)

2013年(12)

2012年(14)

2010年(8)

2009年(52)

我的朋友

分类: C/C++

2009-12-19 13:45:20

关于省略参数的总结
 
 
以下是一些关于省略参数的总结,希望可以同大家分享:
 
省略参数也就是形式如 ReturnType FunctionName (parameterOne,...); 的函数。
参数表应该至少包括一个有名参数,用省略号代替若干个无名参数。无名参数的传递可以通过指针来实现。
 
因为参数列表的参数的地址是连续存放在堆栈里的,所以通过指针式完全可以实现的。
像标准库里面的 int printf(char *fmt, ... )等,也都是通过这个原理来实现的。
 
但是如果要求不调用任何标准库,真正意义的自己去实现这些代码其实是不安全的。这里我们仅仅是为了讨论这个机制,希望能够更深刻的去理解这些知识。如果大家想实现省略参数,建议使用标准库(下面将会介绍)。
 
说了很多,我们还是来写一个简单的小程序来说明问题吧。
 
#include
#include
using namespace std;
int MultiParameterOld(int num,...);  // 自己代码实现
int MultiParameterNew(int num,...);  // 调用标准库
int main()
{
    char *cpTemp = "this parameter is for test!";
    float fTemp = 12.01f;
    MultiParameterOld(7,'K',110,'E','%',fTemp,220,cpTemp,'V','I');
 MultiParameterNew(7,'K',110,'E','%',fTemp,220,cpTemp,'V','I');
    while(1);
    return 1;
}
 
 
/*
 * 下面的方法并不是很通用,对于遍历参数表的方式,也许会因为机器的不同而不同
 * ( 时间:2009-12-19 地点:杭州晶飞科技有限公司 - 研发办公室 )
 * 当前使用的机器是以下面的方式来遍历的,但,是不是多有windows都这样,还不大清楚
 * 遍历参数时,默认以整形数大小来实现字对齐,地址就是用整形数来存放
 * 对于浮点数或者字符串最好通过指针的形式传递,下面有代码实例可供参考
 *
 * 如果想让代码具有更好的通用性,应该使用标准头文件的一组宏定义来遍历参数列表
 * 对于C 头文件为,该头文件的实现因不同的机器而不同,但提供的接口是一致的
 * 详细的使用和说明见 MultiParameterNew() 函数的头部说明部分
 */
int MultiParameterOld (int num,...)
{
 int *add0 = #
 char *chp1 = (char*)(add0+1);
 int  *ip2  = (int*)(chp1+4);
 char *chp3 = (char*)(ip2+1);
 char *chp4 = (char*)(chp3+4);
 double *fp5 = (double*)(chp4+4);
 int  *ip6  = (int*)(fp5+1);
 char **chp7 = (char**)(ip6+1);
 char *chp8 = (char*)(chp7+1);
 char *chp9 = (char*)(chp8+4);
 cout << "省略参数样板 1.0 \n";
 cout << *chp1 << endl;
 cout << *ip2 << endl;
 cout << *chp3 << endl;
 cout << *chp4 << endl;
 cout << *fp5 << endl;
 cout << *ip6 << endl;
 cout << *chp7 << endl;
 cout << *chp8 << endl;
 cout << *chp9 << endl;
    return 1;
}
 
 

/*
 * 使用 头文件的一组宏定义来实现省略参数详解:
 * va_list 类型用于声明一个变量,该变量将依次引用各参数
 * 宏 va_start 将 ap 初始化为指向第一个无名参数的指针,在使用 ap 前必须调用一次
 * 每次调用 va_arg ,该函数都将返回一个参数,并将ap指向下一个参数
 * 最后必须在函数返回之前调用 va_end ,以完成一些必要的清理工作
 */
int MultiParameterNew (int num,...)
{
 va_list ap;   // used to point to a unnamed parameter one by one
 va_start(ap,num); // make ap pointe to the first unnamed parameter
 cout << "省略参数样板 2.0 \n";
 cout << va_arg(ap,char) << endl;
 cout << va_arg(ap,int) << endl;
 cout << va_arg(ap,char) << endl;
 cout << va_arg(ap,char) << endl;
 cout << va_arg(ap,double) << endl;
 cout << va_arg(ap,int) << endl;
 cout << va_arg(ap,char*) << endl;
 cout << va_arg(ap,char) << endl;
 cout << va_arg(ap,char) << endl;
 va_end(ap);
 return 1;
}
 
看完上面的代码,细心的你估计已经发现了两个问题。
第一:每两个连续无名参数之间一定是间隔 sizeof(int) 的内存空间,也就是一个整形数的内存空间。不管是浮点参数,实型参数,字符参数,还是地址参数。都是一样的空间间隔。
第二:浮点参数传递的时候,不能把地址转换成浮点型指针,只能转换指针指向的实际值为浮点型。
 
我们来针对上面两点进一步做一些说明。
(以下是我个人看法,如果有什么不对的地方,请您务必留言告知,谢谢~)
 
第一点:因为参数表里面存放的全部都是连续的地址值,地址值用用一个 int 型变量来存储的,而 int 型数据是与机器相关的,笔者用的 32 机那么 int 就是 32 位,也就是说 sizeof(int) = 4 。
所以说每两个连续的无名参数间距为 4。
 
第二点:对于实型数据,系统默认的是 double 型,而不是 float 型,大家都知道 C 语言是实现的值拷贝的机制,在拷贝的过程中,就把 float 提升为 double来处理,而 double 占用的内存空间是 float 的两倍。也就是说,本来 sizeof(double) 个单元才能表示的一个实型数据,现在强制只使用 sizeof(float) 个单元,那么当然就不对啦。
也就是说,如果你不想通过地址来传递浮点数,那么请记住,浮点数在值拷贝的过程中提升成为了标准的double 型。要强制转换回来只能对整个值进行操作,不能对指针操作,那样会丢掉sizeof(double)-sizeof(float)个单元的信息。
 
如果网友想转载,请附上以下地址:
 
 
各位网友可以参考以下几个网址,笔者觉得可能对大家是有帮助的。
stdarg.h----函数参数个数不确定时的用法:
调用规范与可变参数表: http://www.vckbase.com/document/viewdoc/?id=1438
函数可变参数表的原理:
 
阅读(1166) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~