通过printf函数简单实现解析不定参数表

代码先行,分析在后。

#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语言中,没有函数重载。所以要想实现不定数目的函数参数,变得比较复杂。了解这个问题,涉及到标准头文件<stdarg,h>几个宏定义,

va_start和va_end 等。在此之前,先看一下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.避免出现异常.注意:,应该养成,关闭指针的习惯.

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值