实现可变参数函数常用宏及其作用

stdarg.hC语言中C标准函数库的头文件,stdarg是由standard(标准) arguments(参数)简化而来,主要目的为让函数能够接收可变参数。C++cstdarg头文件中也提供这样的功能;虽然与C的头文件是兼容的,但是也有冲突存在。可变参数函数(Variadic functions)stdarg.h内容典型的应用,虽然也可以使用在其他由可变参数函数调用的函数(例如vprintf)。(参加http://baike.baidu.com/link?url=DA_U06PN2MwV09FoE2YXODNhl0P4xmHXgjM-vNzcnkMGFyLdvvP1KLkyZ8GYpiZLNUIh_WG5cqvPHNJCM3mnOKX_cieBFZCV4-7cFd1IkMW

 

stdarg.h(vs2013win32平台)的内容:

#pragmaonce

#ifndef _INC_STDARG

#define_INC_STDARG 

#include<crtdefs.h> 

#if !defined (_WIN32)

#error ERROR: Only Win32 targetsupported!

#endif /* !defined (_WIN32) */

#include<vadefs.h>

#ifdef__cplusplus

extern"C" {

#endif /* __cplusplus */


#defineva_start_crt_va_start

#defineva_arg_crt_va_arg

#defineva_end_crt_va_end

 

void_CRTIMP__cdecl _vacopy(_Out_va_list *,_In_va_list);

#defineva_copy(apd, aps) _vacopy(&(apd), aps)

 

#ifdef__cplusplus

}

#endif/* __cplusplus */

#endif /* _INC_STDARG

 

它定义了四个宏:va_start,va_arg, va_end, va_copy

其中,va_start, va_arg,va_end宏定义如下:

#define_crt_va_start(ap,v) ( ap = (va_list)_ADDRESSOF(v) + _INTSIZEOF(v) )

#define_crt_va_arg(ap,t)   ( *(t *)((ap += _INTSIZEOF(t)) - _INTSIZEOF(t)) )

#define_crt_va_end(ap)     ( ap = (va_list)0 )

其中:#define_INTSIZEOF(n)  ( (sizeof(n) + sizeof(int) - 1)& ~(sizeof(int) - 1) )

使用这三个宏时均会用到一个参数ap,它的类型为va_list, va_list的定义如下:

#ifdef_M_CEE_PURE

typedefSystem::ArgIterator va_list;

#else /* _M_CEE_PURE */

typedefcharva_list;

#endif /* _M_CEE_PURE */

在大多系统中,va_list相当于char*,用于存储可变参数的地址。

下面介绍这三个宏的作用:

1)宏va_start(ap,v)的作用是得到第一个可变参数的地址,_ADDRESSOF(v) + _INTSIZEOF(v),这里_ADDRESSOF(v)为最后一个固定参数的地址,_INTSIZEOF(v)为这个固定参数实际占用字节数,故相加后就得到了第一个可变参数的起始地址了,va_start(ap,v)用来将ap指向第一个可变参数的地址。

 

2)宏va_arg(ap,t)的作用是把ap指向当前可变参数的下一个可变参数的地址,并返回当前可变参数的内容,我们来看它的实现。

( *(t *)((ap +=_INTSIZEOF(t)) - _INTSIZEOF(t)) )

首先执行最里面的括号的内容:(ap +=_INTSIZEOF(t)),该表达式等价于ap = ap+ _INTSIZEOF(t),ap指向当前参数的下一个参数,那么表达式变为( *(t *)(ap - _INTSIZEOF(t)) ),此时ap已经指向当前可变参数的下一个可变参数,ap - _INTSIZEOF(t)得到的指针,指向了当前可变参数,但此处ap没有赋值操作,因此,ap不变,仍指向当前可变参数的下一个可变参数。( *(t *)(ap - _INTSIZEOF(t)) )将返回当前可变参数的值;

 

3)宏va_end(ap)的作用是将ap指针关掉,它的实现为( ap = (va_list)0 ),使得ap不再指向可变参数的地址,相当于给它置为NULL,以免出现危险,在自己定义的可变函数的末尾用到,用于释放va_start以及va_arg占用的内存。

 

下面是一个简单的int类型变量相加的例子:

#include<stdio.h>

#include<string.h>

#include<stdarg.h>

void simple_add_fun(intstart, ...)

{

   va_list arg_ptr;

   int sum =start;

   va_start(arg_ptr,start);//使arg_ptr指向第一个可变参数

   do {

  sum += va_arg(arg_ptr,int);   //得到可变参数的值并与sum相加,同时

// arg_ptr下一个可变参数

       if (-1 == *arg_ptr)            // -1作为结束标志

       {

           break;

       }

   } while (true);

   printf("sum: %d\n", sum);

   va_end(arg_ptr);

   return;

}

int main(intargc,char*argv[])

{

   simple_add_fun(100, 200, 3013, -1);

   return 0;

}

 

最后介绍宏va_copy

va_copy(apd, aps)使用较少,用于将va_list类型变量aps赋值给apd。此时,我们可能会有疑问了,直接通过va_list apd = aps这种方式赋值不就行了吗?干嘛需要定义一个va_copy的宏呢?

通过查阅资料发现(参考https://linux.die.net/man/3/va_copy)在有些系统中,va_list类型为一个长度为1的数组,此时需要采用*apd=*aps的赋值方式,故封装一个va_copy宏来自适应地完成赋值工作。相应地,每一个va_copy(apd,aps)需要一个va_end(apd)来配对使用,将apd置为NULL,以免出现危险。

下面是网站c++学习网站(http://www.cplusplus.com)上给出的一个例子,用于打印输入的int值:

/* va_copy example */

#include<stdio.h>     /* printf, vprintf*/

#include<stdlib.h>    /* malloc */

#include<string.h>    /* strlen, strcat */

#include<stdarg.h>    /* va_list, va_start, va_copy, va_arg, va_end */

 

/* print ints until a zero is found: */

void PrintInts(intfirst, ...)

{

   char * buffer;

   constchar * format ="[%d] ";

   int count = 0;

   int val =first;

   va_list vl, vl_count;

   va_start(vl,first);

 

   /* count number of arguments: */

   va_copy(vl_count, vl);

   while (val != 0) {

       val = va_arg(vl_count,int);

       ++count;

   }

   va_end(vl_count);

 

   /* allocate storage for formatstring: */

   buffer = (char*)malloc(strlen(format)*count + 1);

   buffer[0] = '\0';

 

   /* generate format string: */

   for (; count>0; --count) {

       strcat(buffer, format);

   }

 

   /* print integers: */

   printf(format, first);

   vprintf(buffer, vl);

 

   va_end(vl);

}

 

int main()

{

   PrintInts(10, 20, 30, 40, 50, 0);

   return 0;

}

 

结果为

[10] [20] [30] [40] [50] [0]

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值