可变参函数的基础知识

      在以前的学习中我们自定义函数时都是指定参数的个数的,然而在C语言中有的函数的参数个数是可变的,就如在stdio.h的文件中的printf函数和scanf函数的参数就是可变的。我们进入底层可以看到的原型为:int printf(const char *format,...);它的参数中就有固定的参数format部分和可变参数部分(用“...”表示)。而另外我们经常用到的scanf函数的原型为:int scanf(const char *format,...);与printf函数一样也有可变参部分。于是参照上面两个函数可变参函数就可以这样定义:
                                                         返回值类型    函数名(固定参数部分(一个固定参数), ...(可变参数部分));
当定义可变参函数是我们还会用到在stdarg.h中一个类型和三个宏。

va_list定义一个字符串指针,(相当于我们经常用的char *)用来访问参数列表的未确定部分。

第一个宏va_start:初始化va_list定义的变量,使其指向可变参数部分的第一个参数。

第二个宏va_arg:这个宏接受两个参数,一个是va_list定义的变量,另一个是参数列表中下一个参数的类型,va_arg返回这个参                             数,并指向可变参列表的下一个参数。

第三个宏va_end:这个宏接收va_list定义的变量作为参数。当访问完可变参时调用。

下面 就是这三个宏的实现,分析这几行代码,我们就能明白这三个宏是如何工作的了


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,t) ( *(t *)((ap += _INTSIZEOF(t)) -_INTSIZEOF(t)) )

#define va_end(ap) ( ap = (va_list)0 )

现在对于可变参的定义擦不多就这些,下面就让我们来看一下这些参数是如何被访问的。


我们知道在大多数编译器中参数进栈的顺序都是由右向左,并且这些参数都是连续的。栈中的数据有着先进后出的特点
         这就是一种压栈现象,当函数调用开始时va_list  *p定义p指向栈顶数据的首地址,当调用宏va_start(p,a);来初始化时将p向下移动sizeof(a)位,即p = p+sizeof(a),使其指向可变参数部分的第一个参数。
  接着就是调用va_arg这个宏,如:va_arg(p,int)(注:这个int是参数的类型,即可变参列表第一个参数的类型,当再次调用va_arg时第二个参数类型可能会改变,这与可变参数的类型有关),使得  p += sizeof(int)并执行  *(p - sizeof(int))取出可变参列表的第一个参数,接下来依次执行下去,直至取到最后一个参数停止(以上都是通过分析va_arg的汇编得出的结论)。
        

         当将可变参列表的所有参数都访问完后就调用va_end这个宏,它的作用和在这和free一样,将申请的指针释放掉即将p释放(va_end(p) == free(p))。整个可变参部分就访问完毕,即可变参函数就执行完了。

这是目前自己学了可变参函数总结的东西,如有不足之处,欢迎指出!!一起学习一起进步。微笑

猜你喜欢

转载自blog.csdn.net/magic_world_wow/article/details/79532881