应用例子一:
#include
/* 函数名:max
* 功能:返回n个整数中的最大值
* 参数:num:整数的个数 ...:num个输入的整数
* 返回值:求得的最大整数
*/
int max ( int num, ... )
{
int m = -0x7FFFFFFF; /* 32系统中最小的整数 */
va_list ap;
va_start ( ap, num );
for ( int i= 0; i< num; i++ )
{
int t = va_arg (ap, int);
if ( t > m )
{
m = t;
}
}
va_end (ap);
return m;
}
/* 主函数调用max */
int main ( int argc, char* argv[] )
{
int n = max ( 5, 5, 6 ,3 ,8 ,5); /* 求5个整数中的最大值 */
cout << n;
return 0;
}
函数max中首先定义了可变参数表指针ap,而后通过va_start ( ap, num )取得了参数表首地址(赋给了ap),其后的for循环则用来遍历可变参数表。这种遍历方式与我们在数据结构教材中经常看到的遍历方式是类似的。
函数max看起来简洁明了,但是实际上printf的实现却远比这复杂。max函数之所以看起来简单,是因为:
(1) max函数可变参数表的长度是已知的,通过num参数传入;
(2) max函数可变参数表中参数的类型是已知的,都为int型。
而printf函数则没有这么幸运。首先,printf函数可变参数的个数不能轻易的得到,而可变参数的类型也不是固定的,需由格式字符串进行识别(由%f、%d、%s等确定),因此则涉及到可变参数表的更复杂应用。
在编程中应该注意的问题和解决办法
虽然可以通过在堆栈中遍历参数列表来读出所有的可变参数,但是由于不知道可变参数有多少个,什么时候应该结束遍历,如果在堆栈中遍历太多,那么很可能读取一些无效的数据.
解决办法:a.可以在第一个起始参数中指定参数个数,那么就可以在循环还中读取所有的可变参数;b.定义一个结束标记,在调用函数的时候,在最后一个参数中传递这个标记,这样在遍历可变参数的时候,可以根据这个标记结束可变参数的遍历;
指定参数个数的示例代码:
//第一个参数定义可选参数个数,用于循环取初参数内容
void arg_cnt(int cnt, );
int main(int argc,char *argv[])
{
int int_size = _INTSIZEOF(int);
printf("int_size=%d\n", int_size);
arg_cnt(4,1,2,3,4);
return 0;
}
void arg_cnt(int cnt, )
{
int value=0;
int i=0;
int arg_cnt=cnt;
va_list arg_ptr;
va_start(arg_ptr, cnt);
for(i = 0; i < cnt; i++)
{
value = va_arg(arg_ptr,int);
printf("value%d=%d\n", i+1, value);
}
}
指定结束标记的示例代码:
CString AppendString(CString str1,...)//一个连接字符串的函数,参数个数可以动态变化
{
LPCTSTR str=str1;//str需为指针类型,因为va_arg宏返回的是你的参数的指针,但是如果你的参数为int等简 //单类型,则不必为指针,因为变量名实际上即是指针。
CString res;
va_list marker; //你的类型链表
va_start(marker,str1);//初始化你的marker链表
while(str!="ListEnd")//ListEnd:参数的结束标志,十分重要,在实际中需自行指定
{
res+=str;
str=va_arg(marker,CString);//取得下一个指针
}
va_end(marker);//结束,与va_start合用
return res;
}
int main()
{
CString str=AppendString("xu","zhi","hong","ListEnd");
cout< return 0;
}
输出 xuzhihong
CString AppendString(CString str1,...),因为连接字符串的参数可以动态变化,你不知用户要进行连接的字符串个数是多少,所以你可以用…来代替。但是要注意的是你的函数要有一个参数作为标志来表示结束,否则会出错。在下例中用ListEnd作为结束符。还有va_arg返回的是你参数内容的指针。上例在支持MFC程序的console下运行通过
参数类型不确定的情况
虽然可以根据上面两个办法解决读取参数个数的问题,但是如果参数类型都是不定的,该怎么办,如果不知道参数的类型,即使读到了参数也没有办法进行处理.解决办法:可以自定义一些可能出现的参数类型,这样在可变参数列表中,可以可变参数列表中的那类型,然后根据类型,读取可变参数值,并进行准确地转换.传递参数的时候可以这样传递:参数数目,可变参数类型1,可变参数值1,可变参数类型2,可变参数值2,....
这里给出一个完整的例子:
#include
#include
const int INT_TYPE = 100000;
const int STR_TYPE = 100001;
const int CHAR_TYPE = 100002;
const int LONG_TYPE = 100003;
const int FLOAT_TYPE = 100004;
const int DOUBLE_TYPE = 100005;
//第一个参数定义可选参数个数,用于循环取初参数内容
//可变参数采用arg_type,arg_value的形式传递,以处理不同的可变参数类型
void arg_type(int cnt, );
//第一个参数定义可选参数个数,用于循环取初参数内容
void arg_cnt(int cnt, );
//测试va_start,va_arg的使用方法,函数参数在堆栈中的地址分布情况
void arg_test(int i, );
int main(int argc,char *argv[])
{
int int_size = _INTSIZEOF(int);
printf("int_size=%d\n", int_size);
arg_test(0, 4);
arg_cnt(4,1,2,3,4);
arg_type(2, INT_TYPE, 222, STR_TYPE, "ok,hello world!");
return 0;
}
void arg_test(int i, )
{
int j=0;
va_list arg_ptr;
va_start(arg_ptr, i);
printf("&i = %p\n", &i);//打印参数i在堆栈中的地址
printf("arg_ptr = %p\n", arg_ptr);
//打印va_start之后arg_ptr地址,
//应该比参数i的地址高sizeof(int)个字节
//这时arg_ptr指向下一个参数的地址
j=*((int *)arg_ptr);
printf("%d %d\n", i, j);
j=va_arg(arg_ptr, int);
printf("arg_ptr = %p\n", arg_ptr);
//打印va_arg后arg_ptr的地址
//应该比调用va_arg前高sizeof(int)个字节
//这时arg_ptr指向下一个参数的地址
va_end(arg_ptr);
printf("%d %d\n", i, j);
}
void arg_cnt(int cnt, )
{
int value=0;
int i=0;
int arg_cnt=cnt;
va_list arg_ptr;
va_start(arg_ptr, cnt);
for(i = 0; i < cnt; i++)
{
value = va_arg(arg_ptr,int);
printf("value%d=%d\n", i+1, value);
}
}
void arg_type(int cnt, )
{
int arg_type = 0;
int int_value=0;
int i=0;
int arg_cnt=cnt;
char *str_value = NULL;
va_list arg_ptr;
va_start(arg_ptr, cnt);
for(i = 0; i < cnt; i++)
{
arg_type = va_arg(arg_ptr,int);
switch(arg_type)
{
case INT_TYPE:
int_value = va_arg(arg_ptr,int);
printf("value%d=%d\n", i+1, int_value);
break;
case STR_TYPE:
str_value = va_arg(arg_ptr,char*);
printf("value%d=%d\n", i+1, str_value);
break;
default:
break;
}
}
}
应用例子二:
简单的printf函数的实现,参考了中的例子
#include "stdio.h"
#include "stdlib.h"
void myprintf(char* fmt, ) //一个简单的类似于printf的实现,//参数必须都是int 类型
{
char* pArg=NULL; //等价于原来的va_list
char c;
pArg = (char*) &fmt; //注意不要写成p = fmt !!因为这里要对//参数取址,而不是取值
pArg += sizeof(fmt); //等价于原来的va_start
do
{
c =*fmt;
if (c != '%')
{
putchar(c); //照原样输出字符
}
else
{
//按格式字符输出数据
switch(*++fmt)
{
case'd':
printf("%d",*((int*)pArg));
break;
case'x':
printf("%#x",*((int*)pArg));
break;
default:
break;
}
pArg += sizeof(int); //等价于原来的va_arg
}
++fmt;
}while (*fmt != '\0');
pArg = NULL; //等价于va_end
return;
}
int main(int argc, char* argv[])
{
int i = 1234;
int j = 5678;
myprintf("the first test:i=%d\n",i,j);
myprintf("the secend test:i=%d; %x;j=%d;\n",i,0xabcd,j);
system("pause");
return 0;
}
阅读(895) | 评论(0) | 转发(1) |