不确定函数参数个数时的处理,va_start和va_end使用详解

引出

  在C中,当我们无法确定函数的所有实参的类型和数目时,可以用省略号指定参数表来处理,如:

void foo(...);
void foo(parm_list,...);

  具体怎么使用呢,我们首先需要把函数参数的传递原理说清楚。

函数参数的传递原理

  函数参数传递的时候是从右至左依次入栈的,然后调用的时候再依次出栈。先入栈的在栈底,后入栈的在栈顶。在内存中,栈底是高地址,栈顶是低地址。
  举个例子如下:

void func(int x, float y, char z);   

  调用此函数的时候,实参 char z 先进栈,然后是 float y,最后是 int x,因此在内存中变量的存放次序是 x->y->z,从理论上说,我们只要探测到任意一个变量的地址,并且知道其他变量的类型,通过指针移位运算,就可以顺藤摸瓜找到其他的输入变量。

使用方法

  首先需要包含头文件<stdarg.h>,下面是 <stdarg.h> 里面重要的几个宏定义如下:

typedef char* va_list;
void va_start ( va_list ap, prev_param ); /* ANSI version */
type va_arg ( va_list ap, type );
void va_end ( va_list ap );

va_list 是一个字符指针,可以理解为指向当前参数的一个指针,取参必须通过这个指针进行。
<Step 1> 在调用参数表之前,定义一个 va_list 类型的变量,(假设va_list 类型变量被定义为ap);
<Step 2> 然后应该对ap 进行初始化,让它指向可变参数表里面的第一个参数,这是通过 va_start 函数来实现的,该函数的第一个参数是 ap 本身,第二个参数是在变参表前面紧挨着的一个变量,即“…”之前的那个参数;
<Step 3> 然后是获取参数,调用va_arg函数,它的第一个参数是ap,第二个参数是要获取的参数的指定类型,然后返回这个指定类型的值,并且把 ap 的位置指向变参表的下一个变量位置;
<Step 4> 获取所有的参数之后,我们有必要将这个 ap 指针关掉,以免发生危险,方法是调用 va_end函数,他把输入的参数 ap 置为 NULL,应该养成获取完参数表之后关闭指针的习惯。说白了,就是让我们的程序具有健壮性。通常va_start和va_end是成对出现。

使用例子1

#include <stdarg.h>
#include <stdio.h>

int sum(int, ...);

int main(void)
{
   printf("10、20 和 30 的和 = %d\n",  sum(3, 10, 20, 30) );
   printf("4、20、25 和 30 的和 = %d\n",  sum(4, 4, 20, 25, 30) );

   return 0;
}

int sum(int num_args, ...)
{
   int val = 0;
   va_list ap;
   int i;

   va_start(ap, num_args);
   for(i = 0; i < num_args; i++)
   {
      val += va_arg(ap, int);
   }
   va_end(ap);
 
   return val;
}

  当需要对多个数进行和运算,但是数的个数不确定,就可以使用参数表。

使用例子2

#include <stdio.h> 
#include <string.h>
#include <stdarg.h>  

/*函数原型声明,至少需要一个确定的参数,注意括号内的省略号*/
int demo(char *fmt, ...);  

void main()   
{   
   demo("DEMO", "This", "is", "a", "demo!", "");   
}   

int demo( char *fmt, ... )   
{   
   va_list argp;   
   int argno = 0;    
   char *para;           /*argp指向传入的第一个可选参数,msg是最后一个确定的参数*/   

   va_start(argp, fmt);   

   while (1)   
   {   
      para = va_arg(argp, char *);   
      if (strcmp( para, "") == 0)
		break;

      printf("Parameter #%d is: %s \r\n", argno, para);   
	  
      argno++;   
   }   
   va_end( argp );        /*将argp置为NULL*/
   return 0;   
}  
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

【ql君】qlexcel

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值