关于省略参数的总结
以下是一些关于省略参数的总结,希望可以同大家分享:
省略参数也就是形式如 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----函数参数个数不确定时的用法:
函数可变参数表的原理:
阅读(1166) | 评论(0) | 转发(0) |