先看代码,
minprintf
#include<stdarg.h>
/*minprintf:minimal printf with variable argument list */
void minprintf(char *fmt,...)
{
va_list ap; /*points to unnamed arg in turn */
char *p, *sval;
int ival;
double dval;
va_start(ap,fmt); /*make ap point to 1st unamed arg*/
for (p = fmt; *p; p++) {
if (*p != '%') {
putchar(*p);
continue;
}
switch(*++p) {
case 'd':
ival = va_arg(ap ,int);
printf("%d", ival);
break;
case 'f':
ival = va_arg(ap ,double);
printf("%f", ival);
break;
case 's':
for(sval = va_arg(ap,char *);*sval;sval ++)
putchar(*sval);
break;
default:
putchar(*p);
break;
}
}
va_end(ap) ; /*clean up when done*/
}
在C语言中,没有函数重载。所以要想实现不定数目的函数参数,变得比较复杂。了解这个问题,涉及到标准头文件
-
1.在c中,当我们无法列出传递函数的所有实参的类型和数目时,,可以用省略号指定参数列表。
void foo(...);
void foo(parm_list,..);
这就是C中一种传参的形式,多用于变长参数表。
-
2.函数参数传递的原理
函数参数是以数据结构:栈的形式存取,从右至左入栈。这跟栈的机制有关。
参数的内存存放格式:参数存放在内存的堆栈段中,在执行函数时,从最后一个开始入栈。因此栈底的高地址,栈顶低地址。void func(int x,float y,char z),在函数调用的时候,是参char z先进栈,然后是 floaty,intx,出的时候顺序是相反的。理论上说,如果我们找到任意变量的地址,并知道其他变量的类型,便可以通过移动位置(指针移位运算,找到其他的输入变量。
下面看几个宏定义
typedef char* va_list;
void va_start(va_list ap, prev_param):/ANSI version*/
type va_arg(va_list ao,type);
void va_end(va_list ap);
va_list是一个字符指针,可以理解为当前参数的一个指针,取参必须通过这个指针进行。
1在调用参数表之前,定义一个va_list类型的变量(假设va_list类型变量被定义为ap);
2然后应该对ap进行初始化,让它指向可变参数列表的第一个参数,是通过va_start实现的,第一个参数是ap本身,第二个参数是在变参表前面紧挨着的一个变量,即“...”之前的那个参数。
3 获取参数,调用va_arg,他的第一个参数是ap,第二个参数是获取的参数的指定类型,然后返回这个指定类型的值,并且把ap指向参数的下一个变量位置
4 获取所有参加之后,我们有必要将ap指针关掉,调用va_end ,他是将ap指向为空,应该养成取完参数之后,关闭指针的习惯,一般情况下,vstart 和 vend 同时出现。
如下面的小例子
#include <stdio.h>
void fun(int a,..)
{
int *temp = &a;
temp++;
for(int i = 0; i < a; ++i)
{
printf("%d",*temp);
temp++;
}
}
int main()
{
int a=1,
int b=2;
int c=3;
int d=4;
fun(4,a,b,c,d);
return 0;
}
获取省略号指定的参数
在函数体声明一个va_list,然后用va_start函数获取参数列表中的参数,使用完毕后调用va_end()结束,例如:
void TestFun(char *pszDest, int DestLen, const char* pszFormat,...)
{
va_list args;
va_start(args,pszFormat); //"一定要"..."之前的那个参数
_vsnprintf(pszDest,Destlen,pszFormat,args);
va_end(args);
}
5.如何使用参数个数可变的函数,
#include <stdio.h>
#include <string.h>
#include <stdarg.h>
/*注意函数原型声明,至少有一个确定的参数,后面跟省略号*/
int demo(char,...)
void main(void)
{
demo("DEMO","This","is","a","demo!"," ");
}
int demo(char msg,...)
{
/*定义保存函数参数的结构*/
va_list argp;
int argno = 0;
char para;
/*argp 指向第一个可选参数,msg是最后一个确定的参数*/
va_start(argp,msg);
{
while(1)
{
para = va_arg(argp,char);
if(strcmp(para ," ") == 0)
break;
printf("Parameter #%d is :%s\n",argno,para);
argno ++;
}
va_end(argp)
retrun 0;
}
6 回过头来看一下一开始那个程序.
minprintf函数用来遍历printf函数的参数表,它的参数为printf函数中参数的指针 指针ap 用来实现遍历函数的参数列表.在函数运行中ap会先后指向参数表中的每一个参数.
va_start(ap,fmt).ap指向省略中的第一个参数.fmt指向最后一个函数参数表中有名参数.即开始时ap指向的参数的前一个参数.
for(p =fmt;p;p++),p初始为fmt ,即指向ap前一个参数,通过对p的循环实现ap指针遍历省略的参数表.
就有了下面的switch,来讨论可能出现的参数.
最后通过va_end(ap),将ap指向NULL.避免出现异常.注意:,应该养成,关闭指针的习惯.
Top^