不确定函数参数个数时的处理,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_list ap;后面出现了va_start (ap, fmt),va_arg (ap, int)。fmt 和 int 是啥参数啊?
C 语言函数形参当中的 “...“ 是什么意思, va_start()va_arg()va_copy() 和 va_end()