在工作中,一些可变参函数里总能看到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;
}