可变参数列表的解析
函数中除了能传入固定数目的参数,还能接受数量不定的参数。
可变参数列表是通过宏来实现的,这些宏定义于stdarg.h 头文件,它是标准库的一部分。
这个头文件声明了一个类型 va_list 和三个宏 va_start va_arg 和 va_end.
通过可变参数列表计算多个参数的平均值
#include <stdarg.h> #include <stdio.h> float average(int n_values, ...){ va_list arg; //设置访问参数列表的变量 int i = 0; float sum = 0; va_start(arg, n_values); //对可变参数列表进行访问 for (i = 0; i < n_values; i++) sum += va_arg(arg,int); //获取可变参数列表的值 return sum / n_values; va_end(arg); } int main() { int a = 1, b = 2, c = 3, d = 4; int avg1 = average(2, a, c); int avg2 = average(3, a, b, c); printf("%d\n", avg1); printf("%d\n", avg2); return 0; }
转到函数的源码
typedef char * va_list;//va_list 是声明的char*的指针类型 #define _INTSIZEOF(n) ( (sizeof(n) + sizeof(int) - 1) & ~(sizeof(int) - 1) ) #define _crt_va_start(ap,v) ( ap = (va_list)_ADDRESSOF(v) + _INTSIZEOF(v) ) //将声明的arg变量指向可变参数部分的第一个参数 #define _crt_va_arg(ap,t) ( *(t *)((ap += _INTSIZEOF(t)) - _INTSIZEOF(t)) ) //取出每一个参数,t表示下一个参数的类型 #define _crt_va_end(ap) ( ap = (va_list)0 )
va_start
函数声明了一个va_list 类型的变量,用于访问参数列表的未确定的部分。这个变量通过调用va_start进行初始化。va_start(arg , n_values) 它的第一个参数为声明的变量arg ,第二个参数为省略号前最后一个有名字的参数。初始化过程把arg变量设置为指向可变参数部分的第一个参数。
va_arg
这个宏用于访问参数,它接受两个参数:va_list变量和参数列表下一个参数的类型。本例中所有参数都为整型,,但在有些函数中,可能需要通过前面获取的数据来判断下一个参数的类型,例如(printf检查格式字符串中的参数来判断它需要打印的参数的类型)。
va_arg 返回这个参数的值,并使变量arg指向下一个参数。
va_end
当访问完最后一个可变参数之后,我们需要调用va_end。
可变参数的限制
可变参数必须从头到尾按顺序逐个访问,它可以在访问途中停止。但是不能一开始就从中间访问参数。
由于参数列表中的可变参数部分并没有原型,所以,所有作为可变参数传递给函数的值都将执行缺省参数类型提升。
参数列表中至少要有一个命名参数。否则将无法通过va_start查找到可变参数部分。
对于这些宏存在两个基本的限制
这些宏无法判断实际存在参数的数量。
这些宏无法判断每个参数的类型。
要通过使用命名参数才能解决这两个问题,在求平均值的程序中,命名参数指定了可变参数的数量,他们的类型被假定为整型。printf函数中的命名参数是格式字符串,它不仅指定了参数的数量还指定了参数的类型。
#利用可变参数列表求解最大值
#include <stdarg.h> #include <stdio.h> int max(int n, ...) { va_list arg; va_start(arg, n); int max = va_arg(arg, int); int t = 0; n--; while (n--){ t = va_arg(arg, int); if (max < t) max = t; } return max; } int main() { int a = 1, b = 2, c = 3; int ret=max(3, a, b, c); printf("%d", ret); return 0; }
##利用可变参数列表实现printf
#include <stdarg.h> #include <stdio.h> void printfnum(int num) { if (num > 9) printfnum(num/10); putchar(num % 10 + '0'); } print( const char *format, ...) { va_list arg; va_start(arg, format); while (*format) { switch (*format)//首先判断字符的类型,然后将其打印 { case 's': { char *str = format; str = va_arg(arg, char*); while (*str) { putchar(*str); str++; } } break; case 'c': { char ch = va_arg(arg, char); putchar(ch); } break; case 'd': { int ret = va_arg(arg, int); printfnum(ret); //将数字100通过字符的方式一个个的打印出来 } break; default: putchar(*format); break; } format++; } } int main() { print("s ccc d.\n", "hello", 'b', 'i', 't', 100); return 0; }