va_list、va_start、va_end相关

3 篇文章 0 订阅
1 篇文章 0 订阅

原创,专业,图文 vsprintf函数跟valist用法详解 - vsprintf,函数,valist,用法,详解 今日头条,最新,最好,最优秀,最靠谱,最有用,最好看,最有效,最热,排行榜,最牛,怎么办,怎么弄,解决方案,解决方法,怎么处理,如何处理,如何解决vsprintf函数和va_list用法详解 

今天写avr单片机程序,看串口通讯函数:

void writeLog(char *format,...){
    char buf[1024] = {0};//自己指定
    va_list list;
    vsprintf(buf,format,list);
    va_end(list);    
}

不解。查找了一下用法,记录如下:

va_list的用法( va_list 是在c语言中解决变参问题的一组宏):

(1)首先在函数里定义一具va_list型的变量,这个变量是指向参数的指针
(2)然后用va_start宏初始化变量刚定义的va_list变量,这个宏的第二个
参数是第一个可变参数的前一个参数,是一个固定的参数。(如在运行va_start
(ap,v)以后,ap指向第一个可变参数在堆栈的地址。)
(3)然后用va_arg返回可变的参数,va_arg的第二个参数是你要返回的参数的类型。
(4)最后用va_end宏结束可变参数的获取。然后你就可以在函数里使用第二个参数了。
如果函数有多个可变参数的,依次调用va_arg获取各个参数。

函数名: vsprintf 
功 能: 送格式化输出到串中 
用 法: int vsprintf(char *string, char *format, va_list param); 
程序例:

#include 
#include 
#include

char buffer[80];

int vspf(char *fmt, ...) 

va_list argptr; 
int cnt;

va_start(argptr, fmt); 
cnt = vsprintf(buffer, fmt, argptr); 
va_end(argptr);

return(cnt); 
}

int main(void) 

int inumber = 30; 
float fnumber = 90.0; 
char string[4] = "abc";

vspf("%d %f %s", inumber, fnumber, string); 
printf("%s\n", buffer); 
return 0; 
}

 

一直没有搞懂printf函数是怎么实现的,今天又看了一下可变参数列表的函数的编写,大概的了解了一点。反推出原来没想到的一个东西,那就是函数形参列表中的变量在内存中的位置是顺次排列的。
头文件stdarg.h里的几个宏定义就是利用了这么一点,顺次获取多个参数,感觉还是挺笨的一个方法。

先看一个最简单的可变参数列表的函数:
void mytest(int a, ...)
{
     int b;
     va_list ap;
     
     va_start(ap, a);
     b = va_arg(ap, int);
     va_end(ap);
     
     printf("%d, %d", a, b);
}
函数实现打印数字。关键问题是搞懂va_list,va_start,va_arg,va_end几个的意思。
首先是在ads下写的程序,打开stdarg是这个样子:
typedef    int       *va_list[1];
#define    va_start(ap, parmn)          (void)(*(ap) = __va_start(parmn))
#define    va_arg(ap, type)               __va_arg(*(ap), type)
#define    va_end(ap)                       ((void)(*(ap) = 0))
实在找不到__va_start,__va_arg是怎么实现的,只好到keil下面去找,这里的就简单多了:
typedef   char   *va_list;
#define   va_start(ap,v)                 ap = (va_list)&v + sizeof(v)
#define   va_arg(ap,t)                   (((t *)ap)++[0])
#define   va_end(ap)

原来每个平台下面的stdarg头文件的定义都是不相同的。就拿keil那里的来入手。
1. 首先定义一个va_list型的变量ap,也就是char *。
2. va_start(ap, a)宏替换之后就是ap = (va_list)&a + sizeof(a);
    首先取a的地址,即第一个固定参数的地址,然后强制类型转换为va_list,接着后移a的内存大小,把当前这个地址值赋给ap。很明显,就是第一个参数a后面的那个地方,按照上面说的,也就是第一个可变参数。即现在把ap指向第一个可变参数。 
3. b = va_arg(ap, int)宏替换为b = ((int *)ap)++[0]; 
    自加在后,因此是获取第一个参数的值赋给b,然后ap后移一个类型的位置,即指向下一个元素的地址。 
4. va_end(ap),这里什么都没有做,在ads那个版本里是将ap指向null,防止误操作。 

那么按照这种做法,当用mytest(100, 200);调用这个函数时,它将可以打印出100,200。 
如果用mytest(100,200,300);调用这个函数,仍然只是显示100,200。如果要将300打印出来,那么得再使用一次va_arg宏,把它赋值给变量c,把c打印出来。 

所以stdarg里的几个宏只是机械的把每个参数读出来,它甚至不知道现在读的是什么类型的变量,如果要读下一个变量的话得往后跳多少位置,这些都只能靠程序自己来判断指示。 

所以printf里的格式符号"%d,%s,%c"之类的都是人为规定的,得自己编程识别这些格式。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值