本文主要介绍va_start和va_end的使用及原理。
核心思想:只是参数个数可变,但参数的类型可知,用format指出参数是什么类型。
函数原型如下:
#include <stdarg.h>
void va_start(va_list ap, last);
type va_arg(va_list ap, type);
void va_end(va_list ap);
void va_copy(va_list dest, va_list src);
1、在C中,当我们无法列出传递函数的所有实参的类型和数目时,可以用省略号指定参数表
void foo(...);
void foo(parm_list,...);
这种方式和我们以前认识的不大一样,但我们要记住这是C中一种传参的形式,在后面我们就会用到它。
2、函数参数的传递原理
函数参数是以栈的形式存取:如func(x,y,z,…)中的参数从右至左入栈。
首先是参数的内存存放格式:参数存放在内存的堆栈段中。在执行函数的时候,从最后一个(z)开始入栈。因此栈底高地址,栈顶低地址:降栈。举个例子如下:
栈底(物理高地址) | 栈顶(物理低地址) | |||
---|---|---|---|---|
栈中已有内容 | 可变参数 … | z | y | x |
stdarg.h 重要的宏定义
typedef char* va_list;
va_list 是一个字符指针,可以理解为指向当前参数的一个指针,取参必须通过这个指针进行。
void va_start ( va_list ap, prev_param ); /* ANSI version */
功能:让ap指针指向第一个可变参数prev_param 的地址
type va_arg ( va_list ap, type );
功能:让ap指针根据可变参的地址和可变参的类型取出一定长度内存里的数据(如int类型是4字节)
void va_end ( va_list ap );
取参步骤:
1、在调用参数表之前,定义一个 va_list 类型的变量,(假设va_list类型变量被定义为ap);
2、对ap 进行初始化,让它指向可变参数表里面的第一个参数,这是通过va_start 来实现。va_start 第一个参数是 ap 本身,第二个参数是在变参表前面紧挨着的一个变量,即 “ … ” 之前的那个参数(Z);
3、获取参数,调用 va_arg,它的第一个参数是ap,第二个参数是要获取的参数的指定类型,然后返回变参表中这个指定类型的值,并且把ap 的位置指向变参表的下一个变量位置;当变参表中的所有参数都获取完毕时,此函数会返回 NULL。
4、获取所有的参数之后,我们有必要将这个 ap 指针关掉,以免发生危险,方法是调用 va_end,他是输入的参数ap 置为 NULL,应该养成获取完参数表之后关闭指针的习惯。说白了,就是让我们的程序具有健壮性。通常va_start和va_end是成对出现。
3、例程
- 1
#include 〈stdio.h〉
#include 〈string.h〉
#include 〈stdarg.h〉
/* 函数原型声明,至少需要一个确定的参数,注意括号内的省略号 */
int demo( char*, ... );
int main(int argc, char** argv )
{
/*
* 注意第一个参数 “DEMO”,不属于变参列表中的参数,
* 不能够被 va_arg 函数获取。
*/
demo("DEMO", "This", "is","a", "demo!", "");
return 0;
}
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*);
/* 一个一个取出可变参数,直到 para 为 NULL,
* 表明所有的可变参数均已获取到。
*/
if (NULL == para) // va_arg( argp, char);一个一个字符char取出可变参数,直到碰到空字符串‘’‘’
break;
printf("Parameter #%d is: %s\n", argno, para);
argno++;
}
/*将argp置为NULL*/
va_end( argp );
return 0;
}
输出:
(注意例程中第一个参数 “Demo” 不属于可变参数列表,因此不会输出)
Parameter #0 is: This
Parameter #1 is: is
Parameter #2 is: a
Parameter #3 is: demo
- 2
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include <unistd.h>
#include <stdarg.h>
void log_to_console(char *format,...) {
va_list args;
va_start(args,format);
vfprintf(stdout,format,args);
va_end(args);
fprintf(stdout,"\n");
fflush(stdout);
}
int main(int argc, char** argv)
{
log_to_console("%s, %d, %c", "hello", 22, '6');
return 0;
}