va_list可变参数理解(va_start/va_end...)

在工作中,一些可变参函数里总能看到va_start、va_end、vsnprintf、va_list等相关函数,甚是疑惑,到底是干啥的,有什么作用呢?

va = variable argument

先来简单介绍一下可变参数吧,先来看看最最熟悉的printf函数。

int printf(const char *format, ...);这三个点点就是可变参数。

好了,开始介绍va相关内容吧!

1.作用

VA_LIST是在C语言中解决变参问题的一组宏。

有点绕,我的理解,如果你写代码过程中,需要对可变参数进行一些自己的操作时候,就可以用这一套函数。

例如:比如对可变参数进行判断修改等,再比如我的一个程序要打印很多东西出来,但是我只想在程序结束前才全部打印出来,那我就可以重构一个类似printf输出函数,每次输出操作时候就可以把输出内容全存入一个字符指针中。最后再一次打印,后面给代码示例。

 

2.函数[宏定义]

头文件:include<stdarg.h>

emmmmm,我在头文件中只找到了如下内容,然后__builtin_va_start(v,l)就找不到了,额,是不是在哪个.c里我没找到,有了解的可以告诉我哈。

那就查阅网上的资料吧,都差不太多。平常使用都是以为是函数用的,其实是宏定义。

平常使用呢,传参如下:

typedef char * va_list;

va_list ap;//在调用可变参数列表(栈)前需要定义一个va_list类型的变量。

void va_start(va_list ap,formant);//对ap进行初始化,让ap指向可变参数表里第一个参数。

     formant为可变参数...前的参数,比如printf,可变参数前一个参数就是formant。

type va_arg(va_list ap,type);//获取参数。

    type为要获取的参数的指定类型,然后返回这个指定类型的值。

void va_end(va_list ap);//清除这个ap指针,相当于平常的free了,不然容易造成泄露问题。

来一个示例:把输入的5个整形参数再输出出来

#include<stdio.h>
#include<stdarg.h>
void va_int_print(int count,...)
{
	int i=0;
	int arg=0;
	va_list args;
	va_start(args,count);
	for(i=0;i<count;i++)
	{
		arg=va_arg(args,int);
		printf("i=%d,arg=%d\n",i,arg);
	}
	va_end(args);
}
int main()
{
	va_int_print(5,11,22,33,44,55);
	return 0;
}

查阅资料,函数传参是以数据结构:栈的形式存取,从右至左入栈的。 

用上图的示例来解释呢,从栈底到栈顶,依次为55(栈底),44,33,22,11,5(count,栈顶),va_start的作用就是把args指向count后一个参数,即可变参数的第一个参数。

再深入一点,因为我没在源码里找到源代码,就搬运百度百科吧T-T

_INTSIZEOF(n)是为了内存对齐,可以保证为int的整数倍。

 

3.使用

再介绍几个常用的函数吧,_vsnprintf和vsprintf。

int vsprintf(char *string,char *format,va_list param);

int _vsnprintf(char *str,size_t size,const char * format,va_list ap);

这俩函数相似于sprintf和snprintf,只不过参数用了VA_LIST

上一个代码吧,就是开头说的,把多次输出的内容都存入到一个buffer指针中,最后再一次性输出,还涉及到内存的realloc,所以这里就只展示一下如何使用VA_LIST,详细代码在我另外一篇介绍realloc的帖子里第6节展示。realloc动态内存调整探究

int ckx_sprintf (char** buf_p,int offset,int *total_size,const char *format, ...)
{
	va_list args;
	int len = 0;
	int available_size=0;
	int total_size_temp = 0;
	char *buf = *buf_p;//临时存一份,如果地址改动,结尾再赋值回去.
	char *r_buf = NULL;
	total_size_temp = *total_size;//当前buf总空间
	available_size = total_size_temp - offset;//当前buf可用空间
	va_start (args, format);
	//linux环境写入长度大于available_size,len还是为format长度
	//windows环境下返回-1
	len = vsnprintf (buf+offset, available_size, format, args);
	va_end (args);
	printf("len = %d total_size_temp = %d available_size = %d offset = %d\n",len,total_size_temp,available_size,offset);
	if (len < 0 || len >= available_size)//如果出错或长度大于当前可用空间
	{
		while (1)
		{
			if (len > -1)
				total_size_temp = len + 1+total_size_temp;
			else
				total_size_temp = total_size_temp *2;

			r_buf = (char*)realloc(buf, total_size_temp);
			if(!r_buf)
				return -1;
			if(r_buf != buf)
			{
				buf = r_buf;//重新开辟了空间就将新指针赋值给旧指针。
			}
			buf[offset] = '\0';
			*total_size = total_size_temp;
			available_size = total_size_temp - offset;
			va_start (args, format);
			len = vsnprintf (buf+offset, available_size, format, args);
			va_end (args);
			if (len > -1 && len < total_size_temp)//如果空间还是不够则继续循环
				break;
		}
	}
	*buf_p = buf;
	return len;
}

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值