C 可变参数表基本内容探讨
创始人
2024-06-22 14:50:36
0

C++编程语言可以被看做C语言的一个升级版本。既然是基于C语言的一个编程语言,当然具有C语言中的所有功能。那么还有那些不同之处呢?其中就有一个不同于其它语言的特性,即其支持C++可变参数表,典型的函数如printf、scanf等可以接受数量不定的参数。如:

 

  1. printf ( "I love you" );   
  2. printf ( "%d", a );  
  3. printf ( "%d,%d", a, b );  

第一、二、三个printf分别接受1、2、3个参数,让我们看看printf函数的原型:

 

  1. int printf ( const char *format, ... );  

从函数原型可以看出,其除了接收一个固定的参数format以外,后面的参数用"…"表示。在C/C++语言中,"…"表示可以接受不定数量的参数,理论上来讲,可以是0或0以上的n个参数。

本文将对C++可变参数表的使用方法及C/C++支持可变参数表的深层机理进行探索。

C++可变参数表的用法

1、相关宏

标准C/C++包含头文件stdarg.h,该头文件中定义了如下三个宏:

 

  1. void va_start ( va_list arg_ptr, prev_param ); /* ANSI version */  
  2. type va_arg ( va_list arg_ptr, type );   
  3. void va_end ( va_list arg_ptr );  

在这些宏中,va就是variable argument(可变参数)的意思;arg_ptr是指向可变参数表的指针;prev_param则指可变参数表的前一个固定参数;type为可变参数的类型。va_list也是一个宏,其定义为typedef char * va_list,实质上是一char型指针。char型指针的特点是++、--操作对其作用的结果是增1和减1(因为sizeof(char)为1),与之不同的是int等其它类型指针的++、--操作对其作用的结果是增sizeof(type)或减sizeof(type),而且sizeof(type)大于1。

通过va_start宏我们可以取得C++可变参数表的首指针,这个宏的定义为:

 

  1. #define va_start ( ap, v ) ( ap = (va_list)&v + _INTSIZEOF(v) )  

显而易见,其含义为将最后那个固定参数的地址加上可变参数对其的偏移后赋值给ap,这样ap就是可变参数表的首地址。其中的_INTSIZEOF宏定义为:

 

  1. #define _INTSIZEOF(n) ((sizeof ( n ) + sizeof ( int ) - 1 )
     & ~( sizeof( int ) - 1 ) )  

va_arg宏的意思则指取出当前arg_ptr所指的可变参数并将ap指针指向下一可变参数,其原型为:

 

  1. #define va_arg(list, mode) ((mode *)(list =\  
  2. (char *) ((((int)list + (__builtin_alignof(mode)<=4?3:7)) &\  
  3. (__builtin_alignof(mode)<=4?-4:-8))+sizeof(mode))))[-1]  

 

对这个宏的具体含义我们将在后面深入讨论。

而va_end宏被用来结束C++可变参数表的获取,其定义为:

 

  1. #define va_end ( list )  

可以看出,va_end ( list )实际上被定义为空,没有任何真实对应的代码,用于代码对称,与va_start对应;另外,它还可能发挥代码的"自注释"作用。所谓代码的"自注释",指的是代码能自己注释自己。

下面我们以具体的例子来说明以上三个宏的使用方法。

2、一个简单的例子

 

  1. #include  
  2. /* 函数名:max  
  3. * 功能:返回n个整数中的最大值  
  4. * 参数:num:整数的个数 ...:num个输入的整数  
  5. * 返回值:求得的最大整数  
  6. */  
  7. int max ( int num, ... )  
  8. {  
  9. int m = -0x7FFFFFFF; /* 32系统中最小的整数 */  
  10. va_list ap;  
  11. va_start ( ap, num );  
  12. for ( int i= 0; i< num; i++ )  
  13. {  
  14. int t = va_arg (ap, int);  
  15. if ( t > m )  
  16. {  
  17. m = t;  
  18. }  
  19. }  
  20. va_end (ap);  
  21. return m;  
  22. }   
  23. /* 主函数调用max */  
  24. int main ( int argc, char* argv[] )  
  25. {   
  26. int n = max ( 5, 5, 6 ,3 ,8 ,5); /* 求5个整数中的最大值 */  
  27. cout << n;  
  28. return 0;  
  29. }  

 

函数max中首先定义了C++可变参数表指针ap,而后通过va_start ( ap, num )取得了参数表首地址(赋给了ap),其后的for循环则用来遍历可变参数表。这种遍历方式与我们在数据结构教材中经常看到的遍历方式是类似的。#t#

函数max看起来简洁明了,但是实际上printf的实现却远比这复杂。max函数之所以看起来简单,是因为:

(1) max函数可变参数表的长度是已知的,通过num参数传入;

(2) max函数可变参数表中参数的类型是已知的,都为int型。

而printf函数则没有这么幸运。首先,printf函数可变参数的个数不能轻易的得到,而可变参数的类型也不是固定的,需由格式字符串进行识别(由%f、%d、%s等确定),因此则涉及到C++可变参数表的更复杂应用。

相关内容

热门资讯

如何允许远程连接到MySQL数... [[277004]]【51CTO.com快译】默认情况下,MySQL服务器仅侦听来自localhos...
如何利用交换机和端口设置来管理... 在网络管理中,总是有些人让管理员头疼。下面我们就将介绍一下一个网管员利用交换机以及端口设置等来进行D...
施耐德电气数据中心整体解决方案... 近日,全球能效管理专家施耐德电气正式启动大型体验活动“能效中国行——2012卡车巡展”,作为该活动的...
Windows恶意软件20年“... 在Windows的早期年代,病毒游走于系统之间,偶尔删除文件(但被删除的文件几乎都是可恢复的),并弹...
20个非常棒的扁平设计免费资源 Apple设备的平面图标PSD免费平板UI 平板UI套件24平图标Freen平板UI套件PSD径向平...
德国电信门户网站可实时显示全球... 德国电信周三推出一个门户网站,直观地实时提供其安装在全球各地的传感器网络检测到的网络攻击状况。该网站...
着眼MAC地址,解救无法享受D... 在安装了DHCP服务器的局域网环境中,每一台工作站在上网之前,都要先从DHCP服务器那里享受到地址动...
为啥国人偏爱 Mybatis,... 关于 SQL 和 ORM 的争论,永远都不会终止,我也一直在思考这个问题。昨天又跟群里的小伙伴进行...