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

Posted qlexcel

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了不确定函数参数个数时的处理,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;   
}  

以上是关于不确定函数参数个数时的处理,va_start和va_end使用详解的主要内容,如果未能解决你的问题,请参考以下文章

va_start和va_end vsnprintf和snprintf

va_start和va_end的使用及原理

__VA_ARGS__和va_start作用, 关于log

C语言中可变参数的函数(三个点,“...”)

前面声明过va_list ap;后面出现了va_start (ap, fmt),va_arg (ap, int)。fmt 和 int 是啥参数啊?

C 语言函数形参当中的 “...“ 是什么意思, va_start()va_arg()va_copy() 和 va_end()