va_list原理及用法

本文详细介绍了C语言中VA_LIST的使用方法及其实现原理,包括如何定义VA_LIST类型的变量、初始化变量、获取可变参数等内容,并通过示例程序解释了va_start、va_arg和va_end等宏的作用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

VA_LIST 是在C语言中解决变参问题的一组宏,变参问题是指参数的个数不定,可以是传入一个参数也可以是多个;可变参数中的每个参数的类型可以不同,也可以相同;可变参数的每个参数并没有实际的名称与之相对应,用起来是很灵活。

下面是va_list的用法示例 :

#include <stdarg.h> 


int AveInt(int,...); 

 void main() 
    {    

       printf("%d/t",AveInt(2,2,3)); 

       printf("%d/t",AveInt(4,2,4,6,8)); 

       return; 
    } 

int AveInt(int v,...) 

{ 

   int ReturnValue=0; 

   int i=v; 

   va_list ap ; 

   va_start(ap,v);  

   while(i>0) 

   { 

          ReturnValue+=va_arg(ap,int) ; 

          i--; 

   } 

   va_end(ap); 


   return ReturnValue/=v; 

} 

VA_LIST的用法:
(1)首先在函数里定义一具VA_LIST型的变量,这个变量是指向参数的指针;
(2)然后用VA_START宏初始化变量,即刚定义的VA_LIST变量;
(3)然后用VA_ARG返回可变的参数,VA_ARG的第二个参数是返回的参数的类型(如果函数有多个可变参数的,依次调用VA_ARG获取各个参数);
(4)最后用VA_END宏结束可变参数的获取。

上面是va_list的具体用法,下面讲解一下va_list各个语句含义和va_list的实现。

可变参数是由宏实现的,但是由于硬件平台的不同,编译器的不同,宏的定义也不相同,下面是VC6.0中x86平台的定义  :   

     typedef char * va_list;
     #define _INTSIZEOF(n)    ((sizeof(n)+sizeof(int)-1)&~(sizeof(int) - 1) ) //先加3满足内存足够使用,然后与(!3)进行位与运算,最多使数据少3,这样能较好满足内存对齐的系统
     #define va_start(ap,v)    ( ap = (va_list)&v + _INTSIZEOF(v) )     //ap指向第一个变参的位置,即将第一个变参的地址赋予ap
     #define va_arg(ap,t)       ( *(t *)( (ap += _INTSIZEOF(t)) - _INTSIZEOF(t) ) )   /*获取变参的具体内容,t为变参的类型,如有多个参数,则通过移动ap的指针来获得变参的地址,从而获得内容*/
     #define va_end(ap) ( ap = (va_list)0 )   //清空va_list,即结束变参的获取




 C语言的函数形参是从右向左压入堆栈的,以保证栈顶是第一个参数,而且x86平台内存分配顺序是从高地址到低地址。因此似函数AVEInt(int var1,int var2,...,int varN)内存分配大致上是这样的:(可变参数在中间)
栈区:
 |栈顶             低地址
 |第一个参数var1                  <-- &v
 |第二个参数var2             <-- va_start(ap,v)后ap指向地址       
 |...
 |函数的最后varN
 |...
 |函数的返回地址
 |...
 |栈底    高地址  

va_list ap ; 定义一个va_list变量ap
va_start(ap,v) ;执行ap = (va_list)&v + _INTSIZEOF(v),ap指向参数v之后的那个参数的地址,即 ap指向第一个可变参数在堆栈的地址。
va_arg(ap,t) , ( (t )((ap += _INTSIZEOF(t)) - _INTSIZEOF(t)) )取出当前ap指针所指的值,并使ap指向下一个参数。 ap+= sizeof(t类型),让ap指向下一个参数的地址。然后返回ap-sizeof(t类型)的t类型指针,这正是第一个可变参数在堆栈里的地址。然后 用取得这个地址的内容。

va_end(ap) ; 清空va_list ap。

使用VA_LIST应该注意的问题:
(1)因为va_start, va_arg, va_end等定义成宏,所以它显得很愚蠢,可变参数的类型和个数完全在该函数中由程序代码控制,它并不能智能地识别不同参数的个数和类型. 也就是说,你想实现智能识别可变参数的话是要通过在自己的程序里作判断来实现的.
(2)另外有一个问题,因为编译器对可变参数的函数的原型检查不够严格,对编程查错不利.不利于我们写出高质量的代码。

(3)由于参数的地址用于VA_START宏,所以参数不能声明为寄存器变量,或作为函数或数组类型。 // ???

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值