C/C++可变参数表的浅析

引言

  C/C++语言有一个不同于其它语言的特性,即其支持可变参数,典型的函数如printf

char  name[]  =   " RedLin " ;
int  age  =   25 ;
int  number  =   20104 ;

printf ( 
" Hello world "  ); 
printf ( 
" My name is %s " , name );
printf ( 
" My age is %d ,My number is %d " , age, number );

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

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

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

本文将对C/C++可变参数表的方法进行浅析。
  1、相关宏
标准C/C++包含头文件stdarg.h,该头文件中定义了如下三个宏:

typedef  char   *   va_list;
#define  _INTSIZEOF(n)   ( (sizeof(n) + sizeof(int) - 1) & ~(sizeof(int) - 1) )
#define  va_start(ap,v)  ( ap = (va_list)&v + _INTSIZEOF(v) )
#define  va_arg(ap,type)    ( *(type *)((ap += _INTSIZEOF(type)) - _INTSIZEOF(type)) )
#define  va_end(ap)      ( ap = (va_list)0 ) 

 

对应的函数形式应为:

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

 

在这些宏中, va就是variable argument(可变参数)的意思;arg_ptr是指向可变参数表的指针prev_param则指可变参数表的前一个固定参数;type为可变参数的类型。

为了能从固定参数依次得到每个可变参数,va_start,va_arg充分利用下面两点:

1)        C语言在函数调用时,先将最后一个参数压入栈

2)        X86平台下的内存分配顺序是从高地址内存到低地址内存

高位地址

004010C8:    N个可变参数

。。。

第二个可变参数

第一个可变参数      ? ap

固定参数            ? v

低位地址


结合通过va_start我们可以取得可变参数表的首指针,其含义为将最后那个固定参数的地址加上可变参数对其的偏移后赋值给ap,这样ap就是可变参数表的首地址。

接下来,可以这样设想,如果我能确定这个可变参数的类型,那么我就知道了它占用了多少内存,依葫芦画瓢,我就能得到下一个可变参数的地址。va_arg的意思有俩点:

1)                取出当前ap所指的可变参数

2)                ap指针指向下一可变参数

它先ap指向下一个可变参数,然后减去当前可变参数的大小即得到当前可变参数的内存地址,再做个类型转换,返回它的值。 要确定每个可变参数的类型,有两种做法,要么都是默认的类型,要么就在固定参数中包含足够的信息让程序可以确定每个可变参数的类型。比如,printf,程序通过分析format字符串就可以确定每个可变参数大类型。

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

  下面我们以实例来分析可变参数表的高级应用。

 

///
//  函数名称: MyPrint
//  功能说明: 输出到终端
//  仅整数和字符串,需设置标志(%d、%l、%x、%s)
// 编者:红林
#include  < stdarg.h >
#include 
< iostream >
using   namespace  std;

///
void  MyPrint (  const   char   * format, ... )
{
    
char tempChar;
    
char byLen;
    
    
long dwTemp;
    
int wTemp;
    
char *str;
    
    
int i; 
    va_list lpParam;

    byLen 
= strlen( format );
    va_start ( lpParam, format );

    
for ( i = 0; i < byLen; i++ )
    
{
        
if( format[i] != '%' ) //不是格式符开始
        {
            tempChar 
= format[i];
            cout 
<<tempChar;
        }

        
else
        
{
            
switch (format[i+1])
            
{
                
//整型
                case 'd':
                
case 'D':
                    wTemp 
= va_arg ( lpParam, int );
                    i
++;
                    cout 
<<wTemp;
                
break;
                
                
//长整型
                case 'l':
                
case 'L':
                    dwTemp 
= va_arg ( lpParam, long );
                    i
++;
                    cout 
<<dwTemp;
                
break;
                
                
//16进制(长整型)
                case 'x':
                
case 'X':
                    dwTemp 
= va_arg ( lpParam, long );
                    i
++;
                    cout 
<< hex << dwTemp;
                    cout 
<< dec;
                
break;

                
case 's':
                
case 'S':
                    str 
= va_arg ( lpParam, char*  );
                    i
++;
                    cout 
<<str;
                
break;

                
default:
                
break;
            }

        }

    }

    va_end ( lpParam );
}

在这个函数中,需通过对传入的格式字符串(首地址为lpStr)进行识别来获知可变参数个数及各个可变参数的类型。譬如,在识别为%d后,做的是va_arg ( lpParam, int ),而获知为%l%x后则进行的是va_arg ( lpParam, long )。格式字符串识别完成后,可变参数也就处理结束。

在开始出将printf修改成MyPrint,即可获得同样的输出.如果不是终端输出,则修改cout就可以输出到目标而达到需要的效果 .

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值