带变长参数的函数
带变长参数的函数
很多语言都支持带变长参数的函数,C也不例外,我们常用的比如printf()函数,它的函数原型中参数列表里面有一个省略号,就代表了可变参数列表,可以先看下它的实现:
int printf(char *fmt, ...) { static char sprint_buf[1024]; va_list args; // 初始化指向可变参数列表的指针args int n; va_start(args, fmt); //args指向可变参数表的起始位置 n = vsprintf(sprint_buf, fmt, args); va_end(args); // 将args复位 write(1, sprint_buf, n); return n; }
可能你现在还不明白这段代码,但大概功能能看出来吧,先是把可变参数传给vsprintf,由它负责将内容输出到buf中,然后将buf打印到屏幕上。
但有人可能会对以va_开头的那几个函数可能会比较陌生,下面我列出了它们的函数原型:
#include <stdarg.h> void va_start(va_list ap, argN); type va_arg(va_list ap, type); void va_copy(va_list dest, va_list src); void va_end(va_list ap);
实际上,这几个函数都不是函数,而是宏定义:
typedef char *va_list; #define _AUPBND (sizeof (acpi_native_int) - 1) #define _ADNBND (sizeof (acpi_native_int) - 1) #define _bnd(X, bnd) (((sizeof (X)) + (bnd)) & (~(bnd))) #define va_arg(ap, T) (*(T *)(((ap) += (_bnd (T, _AUPBND))) - (_bnd (T,_ADNBND)))) #define va_end(ap) (void) 0 #define va_start(ap, A) (void) ((ap) = (((char *) &(A)) + (_bnd (A,_AUPBND))))
从上面的宏看好像它们都是在计算一些偏移位置而已。OK,先回过头来看看函数调用的过程,然后就会明白这些偏移值的计算原理。
我们知道计算机中函数调用有两种方式,分别是stdcall与cdecl,它们的区别在于:
1、_stdcall 是Pascal程序的缺省调用方式,通常用于 Win32 API中。按从右至左的顺序压参数入栈。 在主调函数中负责压栈,在被调函数中返回前负责清理栈。 由于被调函数并不知道传进来的参数个数,因此 _stdcall不适合可变长度参数的函数。
2、_cdecl (The C default calling convention)是C/C++ 调用约定,也是按从右至左的顺序压参数入栈,并且由调用者把弹出栈,因此,实现可变参数的函数只能使用该调用约定。
显然,我们的可变参数使用了_cdecl协议调用函数,发生函数调用时,主调函数将参数按照从右往左的顺序压入堆栈,被调函数返回后,主调函数还要对栈进行清理。
看一个简单的例子:
#include <stdarg.h> void fun(int n, ...) { int i, temp; va_list arg; va_start(arg, n);
for (i = 0; i < n; ++i) { temp = va_arg(arg, int); // 取出参数 printf("%d\n", temp); } va_end(arg); } int main() { int a = 1; int b = 2; int c = 3; int d = 4; fun(4, a, b, c, d); return 0; }
首先,声明了一个 va_list(就是char*指针)的参数列表args,
va_list args;
然后用va_start 宏来获取参数列表中的参数,这里args参数是刚声明的一个char*指针,自不必多说,n是位于栈顶的固定参数。
va_start(args, n);
我们知道栈是由高地址向低地址生长,va_start使arg指向了第一个可选参数,
va_arg(args, int);
每次调用va_arg之后(需要指定下一个参数的类型),args就指向下一个参数,这就是前面那一坨宏定义干的事情。
现在我们再来想想printf()函数,它的第一个参数是fmt,根据'%'就能确定后面的可变参数个数和每个参数的类型,这样就能通过va_arg依次从栈中定位到每个参数的位置。
(夜深了,还是先睡吧,未完待续。。。)