可变参数列表_stdarg宏

      可变参数列表是通过宏-stdarg来实现的,这个头文件中声明了一个类型va_list和三个宏va_start,va_arg,va_end,如何正确的理解这一个类型和三个宏呢?通过右击并转到定义处,我们可以发现va_list其实就是一个char *的重命名,函数中是这样写的,typedef char * va_list,这就非常好理解了;对于va_start,va_arg和va_end三个宏我们分别转到定义处可以发现va_start其实是这样的:,而_crt_va_start依然是由define定义的标识符,其实应该是这样  ( ap = (va_list)_ADDRESSOF(v) + _INTSIZEOF(v) );而va_arg和va_start类似也是由define定义的标识符,它的原型应该是这样的:#define _crt_va_arg(ap,t)    ( *(t *)((ap += _INTSIZEOF(t)) - _INTSIZEOF(t)) ),ap就是指向我的可变参数列表中的第一个参数,通过它可以找到之后的参数;对于va_end它的原型应该是这样的#define _crt_va_end(ap)      ( ap = (va_list)0 ),用于结束va_list说指向的参数列表,当访问完最后一个可变参数之后,我们就需要调用va_end来结束指向的参数列表;下面我们来看一个由可变参数实现的求最大值的例子:

int Max(int val,...)
{
 int max=0;
 int i=0;
 va_list arg;
 va_start(arg,val);
 max=va_arg(arg,int);
 for(i=0;i<val-1;i++)
 {
  int tmp=0;
  if((tmp=(va_arg(arg,int)))>max)
   max=tmp;
  else
   max=max;
 }
 va_end(arg);
 return max;
}

 该例子中没有实现主函数的调用。我们可以发现在参数列表处有省略号,它用于提示此处可能传递数量和类型未确定的参数;通过va_arg我们可以访问参数列表中未确定的部分,通过上述转到定义处我们可以发现ap就是上述例子中的arg通过对arg加INTSIZEOF(t)和减去INTSIZEOF(t),arg指针的依然没有发生变化但是整体指向却向后移动查找参数列表中为确定的参数;通过打擂台算法求出所有数的最大值,在程序结束后当然要用va_end来结束arg啦!

    下面我们再来看一个由可变参数列表实现的模拟输出字符串,单个字符和整形数字的例子:

void Print(const char *format,...)
{
 char tmp=0;
 char *str=NULL;
 va_list arg;  //char *
 va_start(arg,format);  //以format查找后面的部分.
 while(*format)
 {
  switch(*format)
  {
  case'c':{          //单个字符
           tmp=va_arg(arg,char);
     putchar(tmp);
    }break;
  case's':         //字符串
   {
    str=va_arg(arg,char *);
    while(*str)
    {
     putchar(*str);
     str++;
    }
   }break;
  case 'd':   //整形数字
   {
    tmp=va_arg(arg,char);
    putchar(tmp);
   }break;
  default:
   {
    putchar(*format);
   }break;
  }
  format++;
 }
 va_end(arg);  //结束标志
}

int main()
{
 Print("s cccd.\n","hello",'b','i','t','2'); //模拟输出hello bit2.
 system("pause");
 return 0;
}

以上代码中没有添加头文件,读者可自行添加,通过模拟实现Print函数对printf有了更加深入的了解,便于实现参数未确定但是也可以实现某些功能,灵活但是也不容易掌握。




你可能感兴趣的:(函数,宏)