在ANSI C中,这些宏的定义位于stdarg.h中:
typedef char *va_list;
va_start宏,获取可变参数列表的第一个参数的地址(list是类型为va_list的指针,param1是可变参数最左边的参数):
#define va_start(list,param1) ( list = (va_list)¶m1+ sizeof(param1) )
va_arg宏,获取可变参数的当前参数,返回指定类型并将指针指向下一参数(mode参数描述了当前参数的类型):
#define va_arg(list,mode) ( (mode *) ( list += sizeof(mode) ) )[-1]
va_end宏,清空va_list可变参数列表:
#define va_end(list) ( list = (va_list)0 )
注:以上sizeof()只是为了说明工作原理,实际实现中,增加的字节数需保证为为int的整数倍
如:#define _INTSIZEOF(n) ( (sizeof(n) + sizeof(int) - 1) & ~(sizeof(int) - 1) )
为了理解这些宏的作用,我们必须先搞清楚:C语言中函数参数的内存布局。首先,函数参数是存储在栈中的,函数参数从右往左依次入栈。
以下面函数为讨论对象:
void test(char *para1,char *param2,char *param3, char *param4) { va_list list; ...... return; }
在linux中,栈由高地址往低地址生长,调用test函数时,其参数入栈情况如下:
当调用va_start(list,param1) 时:list指针指向情况对应下图:
1 #include <stdio.h> 2 #include <stdarg.h> 3 #include <stdlib.h> 4 #include <string.h> 5 6 7 void var_test(char *format, ...) 8 { 9 va_list list; 10 va_start(list,format); 11 char *ch; 12 while(1) 13 { 14 ch = va_arg(list, char *); 15 if(strcmp(ch,"") == 0) 16 { 17 printf("\n"); 18 break; 19 } 20 printf("%s ",ch); 21 } 22 va_end(list); 23 } 24 25 int main() 26 { 27 var_test("test","this","is","a","test",""); 28 return 0; 29 }
1 #include <stdarg.h> 2 #include<stdio.h> 3 4 char buffer[80]; 5 int vspf(char *fmt, ...) 6 { 7 va_list argptr; 8 int cnt; 9 va_start(argptr, fmt); 10 cnt = vsprintf(buffer, fmt, argptr); 11 va_end(argptr); 12 return(cnt); 13 } 14 15 int main(void) 16 { 17 int inumber = 30; 18 float fnumber = 90.0; 19 char string[4] = "abc"; 20 vspf("%d %f %s", inumber, fnumber, string); 21 printf("%s\n", buffer); 22 return 0; 23 }
1 #include <stdio.h> 2 #include <stdarg.h> 3 #include <stdlib.h> 4 #include <string.h> 5 6 int printf_ext(const char* fmt, ...) 7 { 8 char outbuf[128] = {0}; 9 va_list list; 10 //1.将变参转化为字符串 11 va_start(list,fmt); 12 13 vsprintf((char *)outbuf, fmt, list); 14 15 va_end(list); 16 17 //2. 打印字符串到串口 18 int i; 19 for (i=0; i< strlen((char *)outbuf); i++) 20 { 21 putc(outbuf[i]); 22 } 23 return i; 24 } 25 26 27 int main() 28 { 29 printf_ext("test","this","is","a","test",""); 30 return 0; 31 }
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获取各个参数。
VA_ARG函数用于获取一个一个的参数。
vsprintf函数和vsnprintf函数将所有的参数按fmt格式化输出到,buff中。
vsprintf() 中的 arg 参数位于数组中。数组的元素会被插入主字符串的百分比 (%) 符号处。该函数是逐步执行的。在第一个 % 符号中,插入 arg1,在第二个 % 符号处,插入 arg2,依此类推。
原文:https://www.cnblogs.com/alog9/p/12017835.html