va_start, va_arg, va_end 相关

本文主要介绍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)开始入栈。因此栈底高地址,栈顶低地址:降栈。举个例子如下:

栈底(物理高地址)栈顶(物理低地址)
栈中已有内容可变参数 …zyx

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;
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值