va_list、va_start、va_arg、va_end的原理与使用

来源:互联网 发布:淘宝客广告海报 编辑:程序博客网 时间:2024/06/17 12:53

va_list、va_start、va_arg、va_end的原理与使用

va_list,va_start,va_end定义在c库的stdarg.h头文件中

大致定义如下,不同标准的定义不全一样

[cpp] view plaincopy
  1. typedef charva_list;  
  2.   
  3. #define __va_argsiz(t) (((sizeof(t) + sizeof(int) - 1) / sizeof(int)) * sizeof(int))  
  4.   
  5. #define va_start(ap, pN) ((ap) = ((va_list)(&pN) + __va_argsiz(pN)))  
  6.   
  7. #define va_arg(ap, t) (((ap) = (ap) + __va_argsiz(t)), *((t*) (void*) ((ap) - __va_argsiz(t))))  
  8.   
  9. #define va_end(ap) ( ap = (va_list)0 )  

 

__va_argsiz(t)的作用,计算参数占用的机器字长

int 的长度与机器字长相同, 16位的编译器上int长16位,32位的编译器上int长32位...

以32位机为例

char占用一个8位的内存单元

int占用4个8位的内存单元

例子分析固定参数和可变参数的内存布局

[cpp] view plaincopy
  1. //以32位机器为例  
  2. char *e="hello";  
  3. int i=0x12345678;  
  4. printf("i=%d e=%s",i,e);  

参数的内存对齐方式,不考虑大小端.


 

va_list,va_start,va_arg,va_end的

典型的应用例子

[cpp] view plaincopy
  1. #include <stdarg.h>  
  2. #define  MAXARGS     31  
  3.   
  4. /* 
  5.  * execl is called by 
  6.  * execl(file, arg1, arg2, ..., (char *)(0)); 
  7.  */  
  8. int execl(const char *file, const char *args, ...)  
  9. {  
  10.     va_list ap; //定义参数链表  
  11.     char *array[MAXARGS +1];  
  12.     int argno = 0;  
  13.   
  14.     va_start(ap, args); //获取第一个可变参数  
  15.     while (args != 0 && argno < MAXARGS)  
  16.     {  
  17.         array[argno++] = args;  
  18.         args = va_arg(ap, const char *);    //获取下一个可变参数  
  19.     }  
  20.     array[argno] = (char *) 0;  
  21.     va_end(ap); //释放指针  
  22.     return execv(file, array);  
  23. }  


解析1

[cpp] view plaincopy
  1. int execl(const char *file, const char *args, ...)  
  2. {  
  3.     char* ap;  
  4.     char *array[MAXARGS +1];  
  5.     int argno = 0;  
  6.   
  7.     (ap) = ((char*)(&args) + __va_argsiz(args))  
  8.     while (args != 0 && argno < MAXARGS)  
  9.     {  
  10.         array[argno++] = args;  
  11.         args = (((ap) = (ap) + __va_argsiz(const char *)), *((const char **) (void*) ((ap) - __va_argsiz(const char *))))  
  12.     }  
  13.     array[argno] = (char *) 0;  
  14.     ap=(char*)0;  
  15.     return execv(file, array);  
  16. }  

这里运用到了逗号运算符

A=(b,c,d)等价于运算完b,c,d表达式后令A=d

进一步解析

解析2

[cpp] view plaincopy
  1. int execl(const char *file, const char *args, (char *)varg1,(char *)varg2,....)  
  2. {  
  3.     char* ap;  
  4.     char *array[MAXARGS +1];  
  5.     int argno = 0;  
  6.   
  7.     (ap) = ((char*)(&args) + __va_argsiz(args)) //指向固定参数+固定参数大小==指向第一个可变参数  
  8.     while (args != 0 && argno < MAXARGS)  
  9.     {  
  10.         array[argno++] = args;  //填充字串数组  
  11.         (ap) = (ap) + __va_argsiz(const char *) //ap指下一个可变参数  
  12.         args = *((const char **) (void*) ((ap) - __va_argsiz(const char *)))  
  13.     }  
  14.     array[argno] = (char *) 0;  
  15.     ap=(char*)0;        //释放ap指针  
  16.     return execv(file, array);  
  17. }  


 (ap) = ((char*)(&args) + __va_argsiz(args))这里ap指向args的地址+args占用的内存大小,所以ap指向了varg1

进入while循环,进入条件,参数非空,参数个数未超出MAXARGS

array[argno++] = args; -->arry[0]=args 第二次循环array[1]=varg1,第三次循环array[2]=varg2....

(ap) = (ap) + __va_argsiz(const char *)---------->ap指向下一个(const char*)可变参数

args = *((const char **) (void*) ((ap) - __va_argsiz(const char *)))指向本次操作的可变参数,

因为前面(ap) = (ap) + __va_argsiz(const char *)操作,所以其实args实际上等于

args = *((const char **) (void*) ((ap) + __va_argsiz(const char *) - __va_argsiz(const char *)))

化简得

args = *((const char **) (void*) (ap)

接着回到while语句,继续处理下一个可变参数

 用法框架:

[cpp] view plaincopy
  1. xxx func(,const char *args,varg1,varg2,...)  
  2. {  
  3.     va_list  ap;//定义参数链表  
  4.     va_start(ap, args); //获取第一个可变参数  
  5.     循环{  
  6.         处理处理可变参数  
  7.         va_arg(ap,可变参数类型)   //获取下一个可变参数  
  8.     }  
  9.     va_end(ap); //释放指针  
  10.     return xxx;  
  11. }  

原创粉丝点击